[automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into rvc-qpr-dev am: aa9c6c2de6 -s ours am: 6aa534ffa8 -s ours am: 7ec1ed5ccc -s ours
am skip reason: subject contains skip directive
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/14974567
Change-Id: Ie373ab74a720be7418cbd277b295bbf9043365d9
diff --git a/Android.bp b/Android.bp
index 71023bf..670a02c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -327,6 +327,8 @@
],
sdk_version: "core_platform",
static_libs: [
+ // TODO(b/184162091)
+ "android.hardware.soundtrigger3-V1-java",
"bouncycastle-repackaged-unbundled",
"framework-internal-utils",
// If MimeMap ever becomes its own APEX, then this dependency would need to be removed
diff --git a/OWNERS b/OWNERS
index 4970dd1..ccb56d3 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,23 +1,23 @@
# This top-level list should remain narrowly defined as team leads; individual
# teams are strongly encouraged to define narrower OWNERS files at deeper
# levels within the source tree; see OWNERS.md for more details
-akulian@google.com
-dsandler@android.com
-dsandler@google.com
-hackbod@android.com
-hackbod@google.com
-jjaggi@google.com
-jsharkey@android.com
-jsharkey@google.com
-lorenzo@google.com
-michaelwr@google.com
-nandana@google.com
-narayan@google.com
-ogunwale@google.com
-roosa@google.com
-svetoslavganov@android.com
-svetoslavganov@google.com
-yamasani@google.com
+akulian@google.com #{LAST_RESORT_SUGGESTION}
+dsandler@android.com #{LAST_RESORT_SUGGESTION}
+dsandler@google.com #{LAST_RESORT_SUGGESTION}
+hackbod@android.com #{LAST_RESORT_SUGGESTION}
+hackbod@google.com #{LAST_RESORT_SUGGESTION}
+jjaggi@google.com #{LAST_RESORT_SUGGESTION}
+jsharkey@android.com #{LAST_RESORT_SUGGESTION}
+jsharkey@google.com #{LAST_RESORT_SUGGESTION}
+lorenzo@google.com #{LAST_RESORT_SUGGESTION}
+michaelwr@google.com #{LAST_RESORT_SUGGESTION}
+nandana@google.com #{LAST_RESORT_SUGGESTION}
+narayan@google.com #{LAST_RESORT_SUGGESTION}
+ogunwale@google.com #{LAST_RESORT_SUGGESTION}
+roosa@google.com #{LAST_RESORT_SUGGESTION}
+svetoslavganov@android.com #{LAST_RESORT_SUGGESTION}
+svetoslavganov@google.com #{LAST_RESORT_SUGGESTION}
+yamasani@google.com #{LAST_RESORT_SUGGESTION}
# API changes are already covered by API-Review+1 (http://mdb/android-api-council)
# via https://android.git.corp.google.com/All-Projects/+/refs/meta/config/rules.pl.
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index b1c42a9..42ef80c 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -145,9 +145,11 @@
@Test
public void createUser() {
while (mRunner.keepRunning()) {
+ Log.i(TAG, "Starting timer");
final int userId = createUserNoFlags();
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
@@ -156,6 +158,7 @@
@Test
public void createAndStartUser() throws RemoteException {
while (mRunner.keepRunning()) {
+ Log.i(TAG, "Starting timer");
final int userId = createUserNoFlags();
final CountDownLatch latch = new CountDownLatch(1);
@@ -166,6 +169,7 @@
waitForLatch("Failed to achieve ACTION_USER_STARTED for user " + userId, latch);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
@@ -181,12 +185,14 @@
final int userId = createUserNoFlags();
final CountDownLatch latch = new CountDownLatch(1);
registerBroadcastReceiver(Intent.ACTION_USER_STARTED, latch, userId);
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
mIam.startUserInBackground(userId);
waitForLatch("Failed to achieve ACTION_USER_STARTED for user " + userId, latch);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
@@ -200,12 +206,14 @@
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int userId = createUserNoFlags();
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
// Waits for UserState.mUnlockProgress.finish().
startUserInBackgroundAndWaitForUnlock(userId);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
@@ -217,11 +225,13 @@
mRunner.pauseTiming();
final int startUser = mAm.getCurrentUser();
final int userId = createUserNoFlags();
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
switchUser(userId);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
switchUserNoCheck(startUser);
removeUser(userId);
mRunner.resumeTiming();
@@ -237,6 +247,7 @@
final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ true);
final CountDownLatch latch = new CountDownLatch(1);
registerBroadcastReceiver(Intent.ACTION_USER_UNLOCKED, latch, testUser);
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
mAm.switchUser(testUser);
@@ -244,6 +255,7 @@
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
switchUserNoCheck(startUser);
removeUser(testUser);
mRunner.resumeTiming();
@@ -257,11 +269,13 @@
mRunner.pauseTiming();
final int startUser = mAm.getCurrentUser();
final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ false);
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
switchUser(testUser);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
switchUserNoCheck(startUser);
removeUser(testUser);
mRunner.resumeTiming();
@@ -277,11 +291,13 @@
registerBroadcastReceiver(Intent.ACTION_USER_STARTED, latch, userId);
mIam.startUserInBackground(userId);
waitForLatch("Failed to achieve ACTION_USER_STARTED for user " + userId, latch);
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
stopUser(userId, false);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
@@ -295,12 +311,14 @@
final int userId = createUserNoFlags();
final CountDownLatch latch = new CountDownLatch(1);
registerUserSwitchObserver(null, latch, userId);
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
mAm.switchUser(userId);
waitForLatch("Failed to achieve onLockedBootComplete for user " + userId, latch);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
switchUserNoCheck(startUser);
removeUser(userId);
mRunner.resumeTiming();
@@ -326,12 +344,14 @@
}, new IntentFilter(Intent.ACTION_USER_STOPPED));
final CountDownLatch switchLatch = new CountDownLatch(1);
registerUserSwitchObserver(switchLatch, null, startUser);
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
mAm.switchUser(startUser);
waitForLatch("Failed to achieve ACTION_USER_STOPPED for user " + userId, latch);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
try {
switchLatch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
} catch (InterruptedException e) {
@@ -348,9 +368,11 @@
assumeTrue(mHasManagedUserFeature);
while (mRunner.keepRunning()) {
+ Log.i(TAG, "Starting timer");
final int userId = createManagedProfile();
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
attestTrue("Failed creating profile " + userId, mUm.isManagedProfile(userId));
removeUser(userId);
mRunner.resumeTiming();
@@ -365,11 +387,13 @@
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int userId = createManagedProfile();
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
startUserInBackgroundAndWaitForUnlock(userId);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
@@ -386,11 +410,13 @@
// Start the profile initially, then stop it. Similar to setQuietModeEnabled.
startUserInBackgroundAndWaitForUnlock(userId);
stopUser(userId, true);
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
startUserInBackgroundAndWaitForUnlock(userId);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
@@ -408,12 +434,14 @@
final int userId = createManagedProfile();
WindowManagerGlobal.getWindowManagerService().dismissKeyguard(null, null);
installPreexistingApp(userId, DUMMY_PACKAGE_NAME);
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
startUserInBackgroundAndWaitForUnlock(userId);
startApp(userId, DUMMY_PACKAGE_NAME);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
@@ -438,12 +466,14 @@
startApp(userId, DUMMY_PACKAGE_NAME);
stopUser(userId, true);
SystemClock.sleep(1_000); // 1 second cool-down before re-starting profile.
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
startUserInBackgroundAndWaitForUnlock(userId);
startApp(userId, DUMMY_PACKAGE_NAME);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
@@ -457,11 +487,13 @@
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int userId = createManagedProfile();
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
installPreexistingApp(userId, DUMMY_PACKAGE_NAME);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
@@ -478,6 +510,7 @@
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
WindowManagerGlobal.getWindowManagerService().dismissKeyguard(null, null);
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
final int userId = createManagedProfile();
@@ -486,6 +519,7 @@
startApp(userId, DUMMY_PACKAGE_NAME);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
@@ -500,11 +534,13 @@
mRunner.pauseTiming();
final int userId = createManagedProfile();
startUserInBackgroundAndWaitForUnlock(userId);
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
stopUser(userId, true);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
@@ -523,11 +559,13 @@
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int userId = createManagedProfile();
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
startUserInBackgroundAndWaitForUnlock(userId);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
@@ -546,11 +584,13 @@
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int userId = createManagedProfile();
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
startUserInBackgroundAndWaitForUnlock(userId);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java b/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
index e192861..73bff08 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
@@ -266,9 +266,9 @@
public void sendFullStatusReport(Instrumentation instrumentation, String key) {
Log.i(TAG, key + summaryLine());
Bundle status = new Bundle();
- status.putLong(key + "_median", median());
- status.putLong(key + "_mean", mean());
- status.putLong(key + "_min", min());
+ status.putLong(key + "_median (ns)", median());
+ status.putLong(key + "_mean (ns)", mean());
+ status.putLong(key + "_min (ns)", min());
status.putLong(key + "_standardDeviation", standardDeviation());
instrumentation.sendStatus(Activity.RESULT_OK, status);
}
diff --git a/apex/jobscheduler/framework/java/android/app/tare/OWNERS b/apex/jobscheduler/framework/java/android/app/tare/OWNERS
new file mode 100644
index 0000000..217a5ed
--- /dev/null
+++ b/apex/jobscheduler/framework/java/android/app/tare/OWNERS
@@ -0,0 +1 @@
+include /apex/jobscheduler/service/java/com/android/server/tare/OWNERS
\ No newline at end of file
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index 5a13a84..143c0f1 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -804,28 +804,42 @@
@WorkType final int workType) {
final List<StateController> controllers = mService.mControllers;
final int numControllers = controllers.size();
- for (int ic = 0; ic < numControllers; ic++) {
- controllers.get(ic).prepareForExecutionLocked(jobStatus);
- }
- final PackageStats packageStats =
- getPkgStatsLocked(jobStatus.getSourceUserId(), jobStatus.getSourcePackageName());
- packageStats.adjustStagedCount(false, jobStatus.shouldTreatAsExpeditedJob());
- if (!worker.executeRunnableJob(jobStatus, workType)) {
- Slog.e(TAG, "Error executing " + jobStatus);
- mWorkCountTracker.onStagedJobFailed(workType);
+ final PowerManager.WakeLock wl =
+ mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, jobStatus.getTag());
+ wl.setWorkSource(mService.deriveWorkSource(
+ jobStatus.getSourceUid(), jobStatus.getSourcePackageName()));
+ wl.setReferenceCounted(false);
+ // Since the quota controller will start counting from the time prepareForExecutionLocked()
+ // is called, hold a wakelock to make sure the CPU doesn't suspend between that call and
+ // when the service actually starts.
+ wl.acquire();
+ try {
for (int ic = 0; ic < numControllers; ic++) {
- controllers.get(ic).unprepareFromExecutionLocked(jobStatus);
+ controllers.get(ic).prepareForExecutionLocked(jobStatus);
}
- } else {
- mRunningJobs.add(jobStatus);
- mWorkCountTracker.onJobStarted(workType);
- packageStats.adjustRunningCount(true, jobStatus.shouldTreatAsExpeditedJob());
- mActivePkgStats.add(
- jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), packageStats);
- }
- final List<JobStatus> pendingJobs = mService.mPendingJobs;
- if (pendingJobs.remove(jobStatus)) {
- mService.mJobPackageTracker.noteNonpending(jobStatus);
+ final PackageStats packageStats = getPkgStatsLocked(
+ jobStatus.getSourceUserId(), jobStatus.getSourcePackageName());
+ packageStats.adjustStagedCount(false, jobStatus.shouldTreatAsExpeditedJob());
+ if (!worker.executeRunnableJob(jobStatus, workType)) {
+ Slog.e(TAG, "Error executing " + jobStatus);
+ mWorkCountTracker.onStagedJobFailed(workType);
+ for (int ic = 0; ic < numControllers; ic++) {
+ controllers.get(ic).unprepareFromExecutionLocked(jobStatus);
+ }
+ } else {
+ mRunningJobs.add(jobStatus);
+ mWorkCountTracker.onJobStarted(workType);
+ packageStats.adjustRunningCount(true, jobStatus.shouldTreatAsExpeditedJob());
+ mActivePkgStats.add(
+ jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(),
+ packageStats);
+ }
+ final List<JobStatus> pendingJobs = mService.mPendingJobs;
+ if (pendingJobs.remove(jobStatus)) {
+ mService.mJobPackageTracker.noteNonpending(jobStatus);
+ }
+ } finally {
+ wl.release();
}
}
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 96cbed7..848118d 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -141,6 +141,7 @@
*
* Note on locking: Any operations that manipulate {@link #mJobs} need to lock on that object.
* Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}.
+ *
* @hide
*/
public class JobSchedulerService extends com.android.server.SystemService
@@ -199,7 +200,7 @@
};
@VisibleForTesting
- public static Clock sElapsedRealtimeClock = new MySimpleClock(ZoneOffset.UTC) {
+ public static Clock sElapsedRealtimeClock = new MySimpleClock(ZoneOffset.UTC) {
@Override
public long millis() {
return SystemClock.elapsedRealtime();
@@ -599,7 +600,7 @@
KEY_API_QUOTA_SCHEDULE_COUNT, DEFAULT_API_QUOTA_SCHEDULE_COUNT));
API_QUOTA_SCHEDULE_WINDOW_MS = DeviceConfig.getLong(
DeviceConfig.NAMESPACE_JOB_SCHEDULER,
- KEY_API_QUOTA_SCHEDULE_WINDOW_MS, DEFAULT_API_QUOTA_SCHEDULE_WINDOW_MS);
+ KEY_API_QUOTA_SCHEDULE_WINDOW_MS, DEFAULT_API_QUOTA_SCHEDULE_WINDOW_MS);
API_QUOTA_SCHEDULE_THROW_EXCEPTION = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_JOB_SCHEDULER,
KEY_API_QUOTA_SCHEDULE_THROW_EXCEPTION,
@@ -744,9 +745,10 @@
try {
final int userId = UserHandle.getUserId(pkgUid);
IPackageManager pm = AppGlobals.getPackageManager();
- final int state = pm.getApplicationEnabledSetting(pkgName, userId);
+ final int state =
+ pm.getApplicationEnabledSetting(pkgName, userId);
if (state == COMPONENT_ENABLED_STATE_DISABLED
- || state == COMPONENT_ENABLED_STATE_DISABLED_USER) {
+ || state == COMPONENT_ENABLED_STATE_DISABLED_USER) {
if (DEBUG) {
Slog.d(TAG, "Removing jobs for package " + pkgName
+ " in user " + userId);
@@ -762,7 +764,7 @@
"app disabled");
}
}
- } catch (RemoteException|IllegalArgumentException e) {
+ } catch (RemoteException | IllegalArgumentException e) {
/*
* IllegalArgumentException means that the package doesn't exist.
* This arises when PACKAGE_CHANGED broadcast delivery has lagged
@@ -798,16 +800,15 @@
}
}
} else if (Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) {
- int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1);
if (DEBUG) {
- Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
+ Slog.d(TAG, "Removing jobs for " + pkgName + " (uid=" + pkgUid + ")");
}
synchronized (mLock) {
- mUidToPackageCache.remove(uidRemoved);
+ mUidToPackageCache.remove(pkgUid);
// There's no guarantee that the process has been stopped by the time we
// get here, but since this is generally a user-initiated action, it should
// be fine to just put USER instead of UNINSTALL or DISABLED.
- cancelJobsForPackageAndUidLocked(pkgName, uidRemoved,
+ cancelJobsForPackageAndUidLocked(pkgName, pkgUid,
JobParameters.STOP_REASON_USER,
JobParameters.INTERNAL_STOP_REASON_UNINSTALL, "app uninstalled");
for (int c = 0; c < mControllers.size(); ++c) {
@@ -916,8 +917,18 @@
return mConstants;
}
- public boolean isChainedAttributionEnabled() {
- return WorkSource.isChainedBatteryAttributionEnabled(getContext());
+ @NonNull
+ public WorkSource deriveWorkSource(int sourceUid, @Nullable String sourcePackageName) {
+ if (WorkSource.isChainedBatteryAttributionEnabled(getContext())) {
+ WorkSource ws = new WorkSource();
+ ws.createWorkChain()
+ .addNode(sourceUid, sourcePackageName)
+ .addNode(Process.SYSTEM_UID, "JobScheduler");
+ return ws;
+ } else {
+ return sourcePackageName == null
+ ? new WorkSource(sourceUid) : new WorkSource(sourceUid, sourcePackageName);
+ }
}
@Nullable
@@ -1065,7 +1076,7 @@
if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
Slog.w(TAG, "Too many jobs for uid " + uId);
throw new IllegalStateException("Apps may not schedule more than "
- + MAX_JOBS_PER_APP + " distinct jobs");
+ + MAX_JOBS_PER_APP + " distinct jobs");
}
}
@@ -1601,6 +1612,7 @@
/**
* Called when we want to remove a JobStatus object that we've finished executing.
+ *
* @return true if the job was removed.
*/
private boolean stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
@@ -1611,7 +1623,7 @@
// Remove from store as well as controllers.
final boolean removed = mJobs.remove(jobStatus, removeFromPersisted);
if (removed && mReadyToRock) {
- for (int i=0; i<mControllers.size(); i++) {
+ for (int i = 0; i < mControllers.size(); i++) {
StateController controller = mControllers.get(i);
controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
}
@@ -1655,7 +1667,6 @@
* @param failureToReschedule Provided job status that we will reschedule.
* @return A newly instantiated JobStatus with the same constraints as the last job except
* with adjusted timing constraints.
- *
* @see #maybeQueueReadyJobsForExecutionLocked
*/
@VisibleForTesting
@@ -1697,7 +1708,7 @@
newJob.setOriginalLatestRunTimeElapsed(
failureToReschedule.getOriginalLatestRunTimeElapsed());
}
- for (int ic=0; ic<mControllers.size(); ic++) {
+ for (int ic = 0; ic < mControllers.size(); ic++) {
StateController controller = mControllers.get(ic);
controller.rescheduleForFailureLocked(newJob, failureToReschedule);
}
@@ -2116,6 +2127,7 @@
newReadyJobs.clear();
}
}
+
private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor();
/**
@@ -2208,6 +2220,7 @@
runnableJobs.clear();
}
}
+
private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
private void maybeQueueReadyJobsForExecutionLocked() {
@@ -2599,7 +2612,8 @@
* Binder stub trampoline implementation
*/
final class JobSchedulerStub extends IJobScheduler.Stub {
- /** Cache determination of whether a given app can persist jobs
+ /**
+ * Cache determination of whether a given app can persist jobs
* key is uid of the calling app; value is undetermined/true/false
*/
private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
@@ -2880,8 +2894,7 @@
public List<JobInfo> getStartedJobs() {
final int uid = Binder.getCallingUid();
if (uid != Process.SYSTEM_UID) {
- throw new SecurityException(
- "getStartedJobs() is system internal use only.");
+ throw new SecurityException("getStartedJobs() is system internal use only.");
}
final ArrayList<JobInfo> runningJobs;
@@ -2909,8 +2922,7 @@
public ParceledListSlice<JobSnapshot> getAllJobSnapshots() {
final int uid = Binder.getCallingUid();
if (uid != Process.SYSTEM_UID) {
- throw new SecurityException(
- "getAllJobSnapshots() is system internal use only.");
+ throw new SecurityException("getAllJobSnapshots() is system internal use only.");
}
synchronized (mLock) {
final ArrayList<JobSnapshot> snapshots = new ArrayList<>(mJobs.size());
@@ -2972,7 +2984,7 @@
synchronized (mLock) {
boolean foundSome = false;
- for (int i=0; i<mActiveServices.size(); i++) {
+ for (int i = 0; i < mActiveServices.size(); i++) {
final JobServiceContext jc = mActiveServices.get(i);
final JobStatus js = jc.getRunningJobLocked();
if (jc.timeoutIfExecutingLocked(pkgName, userId, hasJobId, jobId, "shell")) {
@@ -3288,7 +3300,7 @@
}
pw.decreaseIndent();
- for (int i=0; i<mControllers.size(); i++) {
+ for (int i = 0; i < mControllers.size(); i++) {
pw.println();
pw.println(mControllers.get(i).getClass().getSimpleName() + ":");
pw.increaseIndent();
@@ -3297,7 +3309,7 @@
}
boolean overridePrinted = false;
- for (int i=0; i< mUidPriorityOverride.size(); i++) {
+ for (int i = 0; i < mUidPriorityOverride.size(); i++) {
int uid = mUidPriorityOverride.keyAt(i);
if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) {
if (!overridePrinted) {
@@ -3364,7 +3376,7 @@
boolean pendingPrinted = false;
pw.println("Pending queue:");
pw.increaseIndent();
- for (int i=0; i<mPendingJobs.size(); i++) {
+ for (int i = 0; i < mPendingJobs.size(); i++) {
JobStatus job = mPendingJobs.get(i);
if (!predicate.test(job)) {
continue;
@@ -3514,7 +3526,8 @@
continue;
}
- job.dump(proto, JobSchedulerServiceDumpProto.RegisteredJob.DUMP, true, nowElapsed);
+ job.dump(proto,
+ JobSchedulerServiceDumpProto.RegisteredJob.DUMP, true, nowElapsed);
proto.write(
JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_READY_TO_BE_EXECUTED,
@@ -3552,7 +3565,7 @@
controller.dumpControllerStateLocked(
proto, JobSchedulerServiceDumpProto.CONTROLLERS, predicate);
}
- for (int i=0; i< mUidPriorityOverride.size(); i++) {
+ for (int i = 0; i < mUidPriorityOverride.size(); i++) {
int uid = mUidPriorityOverride.keyAt(i);
if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) {
long pToken = proto.start(JobSchedulerServiceDumpProto.PRIORITY_OVERRIDES);
@@ -3591,8 +3604,8 @@
if (job == null) {
final long ijToken = proto.start(ActiveJob.INACTIVE);
- proto.write(ActiveJob.InactiveJob.TIME_SINCE_STOPPED_MS,
- nowElapsed - jsc.mStoppedTime);
+ proto.write(ActiveJob.InactiveJob.TIME_SINCE_STOPPED_MS,
+ nowElapsed - jsc.mStoppedTime);
if (jsc.mStoppedReason != null) {
proto.write(ActiveJob.InactiveJob.STOPPED_REASON,
jsc.mStoppedReason);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 3fa1c12..2ebd1ad 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -42,7 +42,6 @@
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.os.WorkSource;
import android.util.EventLog;
import android.util.IndentingPrintWriter;
import android.util.Slog;
@@ -109,6 +108,7 @@
private final Object mLock;
private final IBatteryStats mBatteryStats;
private final JobPackageTracker mJobPackageTracker;
+ private final PowerManager mPowerManager;
private PowerManager.WakeLock mWakeLock;
// Execution state.
@@ -205,6 +205,7 @@
mCallbackHandler = new JobServiceHandler(looper);
mJobConcurrencyManager = concurrencyManager;
mCompletedListener = service;
+ mPowerManager = mContext.getSystemService(PowerManager.class);
mAvailable = true;
mVerb = VERB_FINISHED;
mPreferredUid = NO_PREFERRED_UID;
@@ -271,6 +272,12 @@
// it was inflated from disk with not-yet-coherent delay/deadline bounds.
job.clearPersistedUtcTimes();
+ mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, job.getTag());
+ mWakeLock.setWorkSource(
+ mService.deriveWorkSource(job.getSourceUid(), job.getSourcePackageName()));
+ mWakeLock.setReferenceCounted(false);
+ mWakeLock.acquire();
+
mVerb = VERB_BINDING;
scheduleOpTimeOutLocked();
final Intent intent = new Intent().setComponent(job.getServiceComponent());
@@ -306,6 +313,7 @@
mRunningCallback = null;
mParams = null;
mExecutionStartTimeElapsed = 0L;
+ mWakeLock.release();
mVerb = VERB_FINISHED;
removeOpTimeOutLocked();
return false;
@@ -495,42 +503,10 @@
return;
}
this.service = IJobService.Stub.asInterface(service);
- final PowerManager pm =
- (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
- runningJob.getTag());
- wl.setWorkSource(deriveWorkSource(runningJob));
- wl.setReferenceCounted(false);
- wl.acquire();
-
- // We use a new wakelock instance per job. In rare cases there is a race between
- // teardown following job completion/cancellation and new job service spin-up
- // such that if we simply assign mWakeLock to be the new instance, we orphan
- // the currently-live lock instead of cleanly replacing it. Watch for this and
- // explicitly fast-forward the release if we're in that situation.
- if (mWakeLock != null) {
- Slog.w(TAG, "Bound new job " + runningJob + " but live wakelock " + mWakeLock
- + " tag=" + mWakeLock.getTag());
- mWakeLock.release();
- }
- mWakeLock = wl;
doServiceBoundLocked();
}
}
- private WorkSource deriveWorkSource(JobStatus runningJob) {
- final int jobUid = runningJob.getSourceUid();
- if (WorkSource.isChainedBatteryAttributionEnabled(mContext)) {
- WorkSource workSource = new WorkSource();
- workSource.createWorkChain()
- .addNode(jobUid, null)
- .addNode(android.os.Process.SYSTEM_UID, "JobScheduler");
- return workSource;
- } else {
- return new WorkSource(jobUid);
- }
- }
-
/** If the client service crashes we reschedule this job and clean up. */
@Override
public void onServiceDisconnected(ComponentName name) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
index f741596..7a28407 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
@@ -16,6 +16,9 @@
package com.android.server.job;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED;
+import static android.net.NetworkCapabilities.TRANSPORT_TEST;
+
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import static com.android.server.job.JobSchedulerService.sSystemClock;
@@ -30,6 +33,7 @@
import android.os.Process;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.ArraySet;
import android.util.AtomicFile;
@@ -63,6 +67,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
+import java.util.StringJoiner;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -387,6 +392,36 @@
}
/**
+ * Returns a single string representation of the contents of the specified intArray.
+ * If the intArray is [1, 2, 4] as the input, the return result will be the string "1,2,4".
+ */
+ @VisibleForTesting
+ static String intArrayToString(int[] values) {
+ final StringJoiner sj = new StringJoiner(",");
+ for (final int value : values) {
+ sj.add(String.valueOf(value));
+ }
+ return sj.toString();
+ }
+
+
+ /**
+ * Converts a string containing a comma-separated list of decimal representations
+ * of ints into an array of int. If the string is not correctly formatted,
+ * or if any value doesn't fit into an int, NumberFormatException is thrown.
+ */
+ @VisibleForTesting
+ static int[] stringToIntArray(String str) {
+ if (TextUtils.isEmpty(str)) return new int[0];
+ final String[] arr = str.split(",");
+ final int[] values = new int[arr.length];
+ for (int i = 0; i < arr.length; i++) {
+ values[i] = Integer.parseInt(arr[i]);
+ }
+ return values;
+ }
+
+ /**
* Runnable that writes {@link #mJobSet} out to xml.
* NOTE: This Runnable locks on mLock
*/
@@ -549,15 +584,12 @@
out.startTag(null, XML_TAG_PARAMS_CONSTRAINTS);
if (jobStatus.hasConnectivityConstraint()) {
final NetworkRequest network = jobStatus.getJob().getRequiredNetwork();
- // STOPSHIP b/183071974: improve the scheme for backward compatibility and
- // mainline cleanliness.
- out.attribute(null, "net-capabilities", Long.toString(
- BitUtils.packBits(network.getCapabilities())));
- out.attribute(null, "net-unwanted-capabilities", Long.toString(
- BitUtils.packBits(network.getForbiddenCapabilities())));
-
- out.attribute(null, "net-transport-types", Long.toString(
- BitUtils.packBits(network.getTransportTypes())));
+ out.attribute(null, "net-capabilities-csv", intArrayToString(
+ network.getCapabilities()));
+ out.attribute(null, "net-forbidden-capabilities-csv", intArrayToString(
+ network.getForbiddenCapabilities()));
+ out.attribute(null, "net-transport-types-csv", intArrayToString(
+ network.getTransportTypes()));
}
if (jobStatus.hasIdleConstraint()) {
out.attribute(null, "idle", Boolean.toString(true));
@@ -831,7 +863,14 @@
} catch (NumberFormatException e) {
Slog.d(TAG, "Error reading constraints, skipping.");
return null;
+ } catch (XmlPullParserException e) {
+ Slog.d(TAG, "Error Parser Exception.", e);
+ return null;
+ } catch (IOException e) {
+ Slog.d(TAG, "Error I/O Exception.", e);
+ return null;
}
+
parser.next(); // Consume </constraints>
// Read out execution parameters tag.
@@ -973,31 +1012,79 @@
return new JobInfo.Builder(jobId, cname);
}
- private void buildConstraintsFromXml(JobInfo.Builder jobBuilder, XmlPullParser parser) {
+ /**
+ * In S, there has been a change in format to make the code more robust and more
+ * maintainable.
+ * If the capabities are bits 4, 14, 15, the format in R, it is a long string as
+ * netCapabilitiesLong = '49168' from the old XML file attribute "net-capabilities".
+ * The format in S is the int array string as netCapabilitiesIntArray = '4,14,15'
+ * from the new XML file attribute "net-capabilities-array".
+ * For backward compatibility, when reading old XML the old format is still supported in
+ * reading, but in order to avoid issues with OEM-defined flags, the accepted capabilities
+ * are limited to that(maxNetCapabilityInR & maxTransportInR) defined in R.
+ */
+ private void buildConstraintsFromXml(JobInfo.Builder jobBuilder, XmlPullParser parser)
+ throws XmlPullParserException, IOException {
String val;
+ String netCapabilitiesLong = null;
+ String netForbiddenCapabilitiesLong = null;
+ String netTransportTypesLong = null;
- final String netCapabilities = parser.getAttributeValue(null, "net-capabilities");
- final String netforbiddenCapabilities = parser.getAttributeValue(
- null, "net-unwanted-capabilities");
- final String netTransportTypes = parser.getAttributeValue(null, "net-transport-types");
- if (netCapabilities != null && netTransportTypes != null) {
+ final String netCapabilitiesIntArray = parser.getAttributeValue(
+ null, "net-capabilities-csv");
+ final String netForbiddenCapabilitiesIntArray = parser.getAttributeValue(
+ null, "net-forbidden-capabilities-csv");
+ final String netTransportTypesIntArray = parser.getAttributeValue(
+ null, "net-transport-types-csv");
+ if (netCapabilitiesIntArray == null || netTransportTypesIntArray == null) {
+ netCapabilitiesLong = parser.getAttributeValue(null, "net-capabilities");
+ netForbiddenCapabilitiesLong = parser.getAttributeValue(
+ null, "net-unwanted-capabilities");
+ netTransportTypesLong = parser.getAttributeValue(null, "net-transport-types");
+ }
+
+ if ((netCapabilitiesIntArray != null) && (netTransportTypesIntArray != null)) {
final NetworkRequest.Builder builder = new NetworkRequest.Builder()
.clearCapabilities();
- final long forbiddenCapabilities = netforbiddenCapabilities != null
- ? Long.parseLong(netforbiddenCapabilities)
- : BitUtils.packBits(builder.build().getForbiddenCapabilities());
- // We're okay throwing NFE here; caught by caller
- for (int capability : BitUtils.unpackBits(Long.parseLong(netCapabilities))) {
+
+ for (int capability : stringToIntArray(netCapabilitiesIntArray)) {
builder.addCapability(capability);
}
- for (int forbiddenCapability : BitUtils.unpackBits(
- Long.parseLong(netforbiddenCapabilities))) {
+
+ for (int forbiddenCapability : stringToIntArray(netForbiddenCapabilitiesIntArray)) {
builder.addForbiddenCapability(forbiddenCapability);
}
- for (int transport : BitUtils.unpackBits(Long.parseLong(netTransportTypes))) {
+
+ for (int transport : stringToIntArray(netTransportTypesIntArray)) {
builder.addTransportType(transport);
}
jobBuilder.setRequiredNetwork(builder.build());
+ } else if (netCapabilitiesLong != null && netTransportTypesLong != null) {
+ final NetworkRequest.Builder builder = new NetworkRequest.Builder()
+ .clearCapabilities();
+ final int maxNetCapabilityInR = NET_CAPABILITY_TEMPORARILY_NOT_METERED;
+ // We're okay throwing NFE here; caught by caller
+ for (int capability : BitUtils.unpackBits(Long.parseLong(
+ netCapabilitiesLong))) {
+ if (capability <= maxNetCapabilityInR) {
+ builder.addCapability(capability);
+ }
+ }
+ for (int forbiddenCapability : BitUtils.unpackBits(Long.parseLong(
+ netForbiddenCapabilitiesLong))) {
+ if (forbiddenCapability <= maxNetCapabilityInR) {
+ builder.addForbiddenCapability(forbiddenCapability);
+ }
+ }
+
+ final int maxTransportInR = TRANSPORT_TEST;
+ for (int transport : BitUtils.unpackBits(Long.parseLong(
+ netTransportTypesLong))) {
+ if (transport <= maxTransportInR) {
+ builder.addTransportType(transport);
+ }
+ }
+ jobBuilder.setRequiredNetwork(builder.build());
} else {
// Read legacy values
val = parser.getAttributeValue(null, "connectivity");
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
index e8ebfb5..98e37a1 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
@@ -18,11 +18,9 @@
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
-import android.annotation.Nullable;
import android.app.AlarmManager;
import android.app.AlarmManager.OnAlarmListener;
import android.content.Context;
-import android.os.Process;
import android.os.UserHandle;
import android.os.WorkSource;
import android.util.IndentingPrintWriter;
@@ -62,8 +60,6 @@
private long mNextDelayExpiredElapsedMillis;
private volatile long mLastFiredDelayExpiredElapsedMillis;
- private final boolean mChainedAttributionEnabled;
-
private AlarmManager mAlarmService = null;
/** List of tracked jobs, sorted asc. by deadline */
private final List<JobStatus> mTrackedJobs = new LinkedList<>();
@@ -73,7 +69,6 @@
mNextJobExpiredElapsedMillis = Long.MAX_VALUE;
mNextDelayExpiredElapsedMillis = Long.MAX_VALUE;
- mChainedAttributionEnabled = mService.isChainedAttributionEnabled();
}
/**
@@ -117,7 +112,8 @@
it.add(job);
job.setTrackingController(JobStatus.TRACKING_TIME);
- WorkSource ws = deriveWorkSource(job.getSourceUid(), job.getSourcePackageName());
+ WorkSource ws =
+ mService.deriveWorkSource(job.getSourceUid(), job.getSourcePackageName());
// Only update alarms if the job would be ready with the relevant timing constraint
// satisfied.
@@ -165,7 +161,7 @@
} else if (wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_DEADLINE)) {
// This job's deadline is earlier than the current set alarm. Update the alarm.
setDeadlineExpiredAlarmLocked(job.getLatestRunTimeElapsed(),
- deriveWorkSource(job.getSourceUid(), job.getSourcePackageName()));
+ mService.deriveWorkSource(job.getSourceUid(), job.getSourcePackageName()));
}
}
if (job.hasTimingDelayConstraint()
@@ -177,7 +173,7 @@
&& wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_TIMING_DELAY)) {
// This job's delay is earlier than the current set alarm. Update the alarm.
setDelayExpiredAlarmLocked(job.getEarliestRunTime(),
- deriveWorkSource(job.getSourceUid(), job.getSourcePackageName()));
+ mService.deriveWorkSource(job.getSourceUid(), job.getSourcePackageName()));
}
}
}
@@ -248,7 +244,7 @@
}
}
setDeadlineExpiredAlarmLocked(nextExpiryTime,
- deriveWorkSource(nextExpiryUid, nextExpiryPackageName));
+ mService.deriveWorkSource(nextExpiryUid, nextExpiryPackageName));
}
}
@@ -312,19 +308,7 @@
mStateChangedListener.onControllerStateChanged();
}
setDelayExpiredAlarmLocked(nextDelayTime,
- deriveWorkSource(nextDelayUid, nextDelayPackageName));
- }
- }
-
- private WorkSource deriveWorkSource(int uid, @Nullable String packageName) {
- if (mChainedAttributionEnabled) {
- WorkSource ws = new WorkSource();
- ws.createWorkChain()
- .addNode(uid, packageName)
- .addNode(Process.SYSTEM_UID, "JobScheduler");
- return ws;
- } else {
- return packageName == null ? new WorkSource(uid) : new WorkSource(uid, packageName);
+ mService.deriveWorkSource(nextDelayUid, nextDelayPackageName));
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
new file mode 100644
index 0000000..d53fa20
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.tare;
+
+import android.annotation.NonNull;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.UserHandle;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArrayMap;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
+
+import java.util.List;
+
+/**
+ * Other half of the IRS. The agent handles the nitty gritty details, interacting directly with
+ * ledgers, carrying out specific events such as tax collection and granting initial balances or
+ * replenishing balances, and tracking ongoing events.
+ */
+class Agent {
+ private static final String TAG = "TARE-" + Agent.class.getSimpleName();
+ private static final boolean DEBUG = InternalResourceService.DEBUG
+ || Log.isLoggable(TAG, Log.DEBUG);
+
+ private final CompleteEconomicPolicy mCompleteEconomicPolicy;
+ private final InternalResourceService mIrs;
+
+ @GuardedBy("mLock")
+ private final SparseArrayMap<String, Ledger> mLedgers = new SparseArrayMap<>();
+
+ @GuardedBy("mLock")
+ private long mCurrentNarcsInCirculation;
+
+ Agent(@NonNull InternalResourceService irs,
+ @NonNull CompleteEconomicPolicy completeEconomicPolicy) {
+ mIrs = irs;
+ mCompleteEconomicPolicy = completeEconomicPolicy;
+ }
+
+ @GuardedBy("mLock")
+ @NonNull
+ private Ledger getLedgerLocked(final int userId, @NonNull final String pkgName) {
+ Ledger ledger = mLedgers.get(userId, pkgName);
+ if (ledger == null) {
+ // TODO: load from disk
+ ledger = new Ledger();
+ mLedgers.add(userId, pkgName, ledger);
+ }
+ return ledger;
+ }
+
+ /** Get an app's current balance, factoring in any currently ongoing events. */
+ @GuardedBy("mLock")
+ private long getBalanceLocked(final int userId, @NonNull final String pkgName) {
+ final Ledger ledger = getLedgerLocked(userId, pkgName);
+ long balance = ledger.getCurrentBalance();
+ // TODO: add ongoing events
+ return balance;
+ }
+
+ @GuardedBy("mLock")
+ private void recordTransactionLocked(final int userId, @NonNull final String pkgName,
+ @NonNull Ledger ledger, @NonNull Ledger.Transaction transaction) {
+ final long maxCirculationAllowed = mIrs.getMaxCirculationLocked();
+ final long newArcsInCirculation = mCurrentNarcsInCirculation + transaction.delta;
+ if (transaction.delta > 0 && newArcsInCirculation > maxCirculationAllowed) {
+ final long newDelta = maxCirculationAllowed - mCurrentNarcsInCirculation;
+ Slog.i(TAG, "Would result in too many credits in circulation. Decreasing transaction "
+ + transaction.reason + (transaction.tag == null ? "" : ":" + transaction.tag)
+ + " for <" + userId + ">" + pkgName + " by " + (transaction.delta - newDelta));
+ transaction = new Ledger.Transaction(
+ transaction.startTimeMs, transaction.endTimeMs,
+ transaction.reason, transaction.tag, newDelta);
+ }
+ final long originalBalance = ledger.getCurrentBalance();
+ if (transaction.delta > 0
+ && originalBalance + transaction.delta
+ > mCompleteEconomicPolicy.getMaxSatiatedBalance()) {
+ final long newDelta = mCompleteEconomicPolicy.getMaxSatiatedBalance() - originalBalance;
+ Slog.i(TAG, "Would result in becoming too rich. Decreasing transaction "
+ + transaction.reason + (transaction.tag == null ? "" : ":" + transaction.tag)
+ + " for <" + userId + ">" + pkgName + " by " + (transaction.delta - newDelta));
+ transaction = new Ledger.Transaction(
+ transaction.startTimeMs, transaction.endTimeMs,
+ transaction.reason, transaction.tag, newDelta);
+ }
+ ledger.recordTransaction(transaction);
+ mCurrentNarcsInCirculation += transaction.delta;
+ // TODO: save changes to disk in a background thread
+ final long newBalance = ledger.getCurrentBalance();
+ if (originalBalance <= 0 && newBalance > 0) {
+ mIrs.postSolvencyChanged(userId, pkgName, true);
+ } else if (originalBalance > 0 && newBalance <= 0) {
+ mIrs.postSolvencyChanged(userId, pkgName, false);
+ }
+ }
+
+ @GuardedBy("mLock")
+ void distributeBasicIncomeLocked(int batteryLevel) {
+ List<PackageInfo> pkgs = mIrs.getInstalledPackages();
+ final long now = System.currentTimeMillis();
+ for (int i = 0; i < pkgs.size(); ++i) {
+ final PackageInfo pkgInfo = pkgs.get(i);
+ final int userId = UserHandle.getUserId(pkgInfo.applicationInfo.uid);
+ final String pkgName = pkgInfo.packageName;
+ Ledger ledger = getLedgerLocked(userId, pkgName);
+ final long minBalance = mIrs.getMinBalanceLocked(userId, pkgName);
+ final double perc = batteryLevel / 100d;
+ // TODO: maybe don't give credits to bankrupt apps until battery level >= 50%
+ if (ledger.getCurrentBalance() < minBalance) {
+ final long shortfall = minBalance - getBalanceLocked(userId, pkgName);
+ recordTransactionLocked(userId, pkgName, ledger,
+ new Ledger.Transaction(now, now, "UNIVERSAL_BASIC_INCOME",
+ null, (long) (perc * shortfall)));
+ }
+ }
+ }
+
+ /** Give each app an initial balance. */
+ @GuardedBy("mLock")
+ void grantBirthrightsLocked() {
+ UserManagerInternal userManagerInternal =
+ LocalServices.getService(UserManagerInternal.class);
+ final int[] userIds = userManagerInternal.getUserIds();
+ for (int userId : userIds) {
+ grantBirthrightsLocked(userId);
+ }
+ }
+
+ @GuardedBy("mLock")
+ void grantBirthrightsLocked(final int userId) {
+ PackageManager packageManager = mIrs.getContext().getPackageManager();
+ List<PackageInfo> pkgs = packageManager.getInstalledPackagesAsUser(0, userId);
+ final long maxBirthright =
+ mIrs.getMaxCirculationLocked() / mIrs.getInstalledPackages().size();
+ final long now = System.currentTimeMillis();
+
+ for (int i = 0; i < pkgs.size(); ++i) {
+ final PackageInfo packageInfo = pkgs.get(i);
+ final String pkgName = packageInfo.packageName;
+ final Ledger ledger = getLedgerLocked(userId, pkgName);
+ if (ledger.getCurrentBalance() > 0) {
+ // App already got credits somehow. Move along.
+ Slog.wtf(TAG, "App " + pkgName + " had credits before economy was set up");
+ continue;
+ }
+
+ recordTransactionLocked(userId, pkgName, ledger,
+ new Ledger.Transaction(now, now, "BIRTHRIGHT", null,
+ Math.min(maxBirthright, mIrs.getMinBalanceLocked(userId, pkgName))));
+ }
+ }
+
+ @GuardedBy("mLock")
+ void grantBirthrightLocked(final int userId, @NonNull final String pkgName) {
+ final Ledger ledger = getLedgerLocked(userId, pkgName);
+ if (ledger.getCurrentBalance() > 0) {
+ Slog.wtf(TAG, "App " + pkgName + " had credits as soon as it was installed");
+ // App already got credits somehow. Move along.
+ return;
+ }
+
+ List<PackageInfo> pkgs = mIrs.getInstalledPackages();
+ final int numPackages = pkgs.size();
+ final long maxBirthright = mIrs.getMaxCirculationLocked() / numPackages;
+ final long now = System.currentTimeMillis();
+
+ recordTransactionLocked(userId, pkgName, ledger,
+ new Ledger.Transaction(now, now, "BIRTHRIGHT", null,
+ Math.min(maxBirthright, mIrs.getMinBalanceLocked(userId, pkgName))));
+ }
+
+ @GuardedBy("mLock")
+ void onPackageRemovedLocked(final int userId, @NonNull final String pkgName) {
+ reclaimAssetsLocked(userId, pkgName);
+ }
+
+ /**
+ * Reclaims any ARCs granted to the app, making them available to other apps. Also deletes the
+ * app's ledger and stops any ongoing event tracking.
+ */
+ @GuardedBy("mLock")
+ private void reclaimAssetsLocked(final int userId, @NonNull final String pkgName) {
+ Ledger ledger = getLedgerLocked(userId, pkgName);
+ if (ledger.getCurrentBalance() != 0) {
+ mCurrentNarcsInCirculation -= ledger.getCurrentBalance();
+ }
+ // TODO: delete ledger entry from disk
+ mLedgers.delete(userId, pkgName);
+ }
+
+ @GuardedBy("mLock")
+ void onUserRemovedLocked(final int userId, @NonNull final List<String> pkgNames) {
+ reclaimAssetsLocked(userId, pkgNames);
+ }
+
+ @GuardedBy("mLock")
+ private void reclaimAssetsLocked(final int userId, @NonNull final List<String> pkgNames) {
+ for (int i = 0; i < pkgNames.size(); ++i) {
+ reclaimAssetsLocked(userId, pkgNames.get(i));
+ }
+ }
+
+ @GuardedBy("mLock")
+ void dumpLocked(IndentingPrintWriter pw) {
+ pw.print("Current GDP: ");
+ pw.println(mCurrentNarcsInCirculation);
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
new file mode 100644
index 0000000..744d961
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.tare;
+
+import static com.android.server.tare.Modifier.COST_MODIFIER_CHARGING;
+import static com.android.server.tare.Modifier.COST_MODIFIER_DEVICE_IDLE;
+import static com.android.server.tare.Modifier.COST_MODIFIER_POWER_SAVE_MODE;
+import static com.android.server.tare.Modifier.COST_MODIFIER_PROCESS_STATE;
+import static com.android.server.tare.TareUtils.arcToNarc;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.ArrayMap;
+
+/**
+ * Policy defining pricing information and daily ARC requirements and suggestions for
+ * AlarmManager.
+ */
+public class AlarmManagerEconomicPolicy extends EconomicPolicy {
+ public static final String ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE =
+ "ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE";
+ public static final String ACTION_ALARM_WAKEUP_EXACT = "ALARM_WAKEUP_EXACT";
+ public static final String ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE =
+ "ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE";
+ public static final String ACTION_ALARM_WAKEUP_INEXACT = "ALARM_WAKEUP_INEXACT";
+ public static final String ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE =
+ "ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE";
+ public static final String ACTION_ALARM_NONWAKEUP_EXACT = "ALARM_NONWAKEUP_EXACT";
+ public static final String ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE =
+ "ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE";
+ public static final String ACTION_ALARM_NONWAKEUP_INEXACT = "ALARM_NONWAKEUP_INEXACT";
+ public static final String ACTION_ALARM_CLOCK = "ALARM_CLOCK";
+
+ private static final int[] COST_MODIFIERS = new int[]{
+ COST_MODIFIER_CHARGING,
+ COST_MODIFIER_DEVICE_IDLE,
+ COST_MODIFIER_POWER_SAVE_MODE,
+ COST_MODIFIER_PROCESS_STATE
+ };
+
+ private final ArrayMap<String, Action> mActions = new ArrayMap<>();
+ private final ArrayMap<String, Reward> mRewards = new ArrayMap<>();
+
+ AlarmManagerEconomicPolicy(InternalResourceService irs) {
+ super(irs);
+ loadActions();
+ loadRewards();
+ }
+
+ @Override
+ long getMinSatiatedBalance(final int userId, @NonNull final String pkgName) {
+ // TODO: take exemption into account
+ return arcToNarc(160);
+ }
+
+ @Override
+ long getMaxSatiatedBalance() {
+ return arcToNarc(1440);
+ }
+
+
+ @Override
+ long getMaxSatiatedCirculation() {
+ return arcToNarc(52000);
+ }
+
+ @NonNull
+ @Override
+ int[] getCostModifiers() {
+ return COST_MODIFIERS;
+ }
+
+ @Nullable
+ @Override
+ Action getAction(@NonNull String actionName) {
+ return mActions.get(actionName);
+ }
+
+ @Nullable
+ @Override
+ Reward getReward(@NonNull String rewardName) {
+ return mRewards.get(rewardName);
+ }
+
+ private void loadActions() {
+ mActions.put(ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE,
+ new Action(ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE, arcToNarc(3), arcToNarc(5)));
+ mActions.put(ACTION_ALARM_WAKEUP_EXACT,
+ new Action(ACTION_ALARM_WAKEUP_EXACT, arcToNarc(3), arcToNarc(4)));
+ mActions.put(ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE,
+ new Action(ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE,
+ arcToNarc(3), arcToNarc(4)));
+ mActions.put(ACTION_ALARM_WAKEUP_INEXACT,
+ new Action(ACTION_ALARM_WAKEUP_INEXACT, arcToNarc(3), arcToNarc(3)));
+ mActions.put(ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE,
+ new Action(ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE,
+ arcToNarc(1), arcToNarc(3)));
+ mActions.put(ACTION_ALARM_NONWAKEUP_EXACT,
+ new Action(ACTION_ALARM_NONWAKEUP_EXACT, arcToNarc(1), arcToNarc(2)));
+ mActions.put(ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE,
+ new Action(ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE,
+ arcToNarc(1), arcToNarc(2)));
+ mActions.put(ACTION_ALARM_NONWAKEUP_INEXACT,
+ new Action(ACTION_ALARM_NONWAKEUP_INEXACT, arcToNarc(1), arcToNarc(1)));
+ mActions.put(ACTION_ALARM_CLOCK,
+ new Action(ACTION_ALARM_CLOCK, arcToNarc(5), arcToNarc(10)));
+ }
+
+ private void loadRewards() {
+ mRewards.put(REWARD_TOP_ACTIVITY,
+ new Reward(REWARD_TOP_ACTIVITY,
+ arcToNarc(0), /* .01 arcs */ arcToNarc(1) / 100, arcToNarc(500)));
+ mRewards.put(REWARD_NOTIFICATION_SEEN,
+ new Reward(REWARD_NOTIFICATION_SEEN, arcToNarc(3), arcToNarc(0), arcToNarc(60)));
+ mRewards.put(REWARD_NOTIFICATION_INTERACTION,
+ new Reward(REWARD_NOTIFICATION_INTERACTION,
+ arcToNarc(5), arcToNarc(0), arcToNarc(500)));
+ mRewards.put(REWARD_WIDGET_INTERACTION,
+ new Reward(REWARD_WIDGET_INTERACTION, arcToNarc(10), arcToNarc(0), arcToNarc(500)));
+ mRewards.put(REWARD_OTHER_USER_INTERACTION,
+ new Reward(REWARD_OTHER_USER_INTERACTION,
+ arcToNarc(10), arcToNarc(0), arcToNarc(500)));
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/ChargingModifier.java b/apex/jobscheduler/service/java/com/android/server/tare/ChargingModifier.java
new file mode 100644
index 0000000..a627de6
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/ChargingModifier.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.tare;
+
+import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.SystemClock;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+import android.util.Slog;
+
+/** Modifier that makes things free when the device is charging. */
+class ChargingModifier extends Modifier {
+ private static final String TAG = "TARE-" + ChargingModifier.class.getSimpleName();
+ private static final boolean DEBUG = InternalResourceService.DEBUG
+ || Log.isLoggable(TAG, Log.DEBUG);
+
+ private final InternalResourceService mIrs;
+ private final ChargingTracker mChargingTracker;
+
+ ChargingModifier(@NonNull InternalResourceService irs) {
+ super();
+ mIrs = irs;
+ mChargingTracker = new ChargingTracker();
+ mChargingTracker.startTracking(irs.getContext());
+ }
+
+ @Override
+ long getModifiedCostToProduce(long ctp) {
+ return modifyValue(ctp);
+ }
+
+ @Override
+ long getModifiedPrice(long price) {
+ return modifyValue(price);
+ }
+
+ private long modifyValue(long val) {
+ if (mChargingTracker.mCharging) {
+ return 0;
+ }
+ return val;
+ }
+
+ @Override
+ void dump(IndentingPrintWriter pw) {
+ pw.print("charging=");
+ pw.println(mChargingTracker.mCharging);
+ }
+
+ private final class ChargingTracker extends BroadcastReceiver {
+ /**
+ * Track whether we're "charging", where charging means that we're ready to commit to
+ * doing work.
+ */
+ private volatile boolean mCharging;
+
+ public void startTracking(@NonNull Context context) {
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(BatteryManager.ACTION_CHARGING);
+ filter.addAction(BatteryManager.ACTION_DISCHARGING);
+ context.registerReceiver(this, filter);
+
+ // Initialise tracker state.
+ final BatteryManager batteryManager = context.getSystemService(BatteryManager.class);
+ mCharging = batteryManager.isCharging();
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (BatteryManager.ACTION_CHARGING.equals(action)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Received charging intent, fired @ "
+ + SystemClock.elapsedRealtime());
+ }
+ if (!mCharging) {
+ mCharging = true;
+ mIrs.onDeviceStateChanged();
+ }
+ } else if (BatteryManager.ACTION_DISCHARGING.equals(action)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Disconnected from power.");
+ }
+ if (mCharging) {
+ mCharging = false;
+ mIrs.onDeviceStateChanged();
+ }
+ }
+ }
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java
new file mode 100644
index 0000000..376a75c
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.tare;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
+
+import libcore.util.EmptyArray;
+
+
+/** Combines all enabled policies into one. */
+public class CompleteEconomicPolicy extends EconomicPolicy {
+ private final ArraySet<EconomicPolicy> mEnabledEconomicPolicies = new ArraySet<>();
+ /** Lazily populated set of actions covered by this policy. */
+ private final ArrayMap<String, Action> mActions = new ArrayMap<>();
+ /** Lazily populated set of rewards covered by this policy. */
+ private final ArrayMap<String, Reward> mRewards = new ArrayMap<>();
+ private final int[] mCostModifiers;
+ private final long mMaxSatiatedBalance;
+ private final long mMaxSatiatedCirculation;
+
+ CompleteEconomicPolicy(@NonNull InternalResourceService irs) {
+ super(irs);
+ mEnabledEconomicPolicies.add(new AlarmManagerEconomicPolicy(irs));
+ mEnabledEconomicPolicies.add(new JobSchedulerEconomicPolicy(irs));
+
+ ArraySet<Integer> costModifiers = new ArraySet<>();
+ for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
+ final int[] sm = mEnabledEconomicPolicies.valueAt(i).getCostModifiers();
+ for (int s : sm) {
+ costModifiers.add(s);
+ }
+ }
+ mCostModifiers = new int[costModifiers.size()];
+ for (int i = 0; i < costModifiers.size(); ++i) {
+ mCostModifiers[i] = costModifiers.valueAt(i);
+ }
+
+ long max = 0;
+ for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
+ max += mEnabledEconomicPolicies.valueAt(i).getMaxSatiatedBalance();
+ }
+ mMaxSatiatedBalance = max;
+
+ max = 0;
+ for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
+ max += mEnabledEconomicPolicies.valueAt(i).getMaxSatiatedCirculation();
+ }
+ mMaxSatiatedCirculation = max;
+ }
+
+ @Override
+ public long getMinSatiatedBalance(final int userId, @NonNull final String pkgName) {
+ long min = 0;
+ for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
+ min += mEnabledEconomicPolicies.valueAt(i).getMinSatiatedBalance(userId, pkgName);
+ }
+ return min;
+ }
+
+ @Override
+ public long getMaxSatiatedBalance() {
+ return mMaxSatiatedBalance;
+ }
+
+ @Override
+ public long getMaxSatiatedCirculation() {
+ return mMaxSatiatedCirculation;
+ }
+
+ @NonNull
+ @Override
+ public int[] getCostModifiers() {
+ return mCostModifiers == null ? EmptyArray.INT : mCostModifiers;
+ }
+
+ @Nullable
+ @Override
+ public Action getAction(@NonNull String actionName) {
+ if (mActions.containsKey(actionName)) {
+ return mActions.get(actionName);
+ }
+
+ long ctp = 0, price = 0;
+ boolean exists = false;
+ for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
+ Action a = mEnabledEconomicPolicies.valueAt(i).getAction(actionName);
+ if (a != null) {
+ exists = true;
+ ctp += a.costToProduce;
+ price += a.basePrice;
+ }
+ }
+ final Action action = exists ? new Action(actionName, ctp, price) : null;
+ mActions.put(actionName, action);
+ return action;
+ }
+
+ @Nullable
+ @Override
+ public Reward getReward(@NonNull String rewardName) {
+ if (mRewards.containsKey(rewardName)) {
+ return mRewards.get(rewardName);
+ }
+
+ long instantReward = 0, ongoingReward = 0, maxReward = 0;
+ boolean exists = false;
+ for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
+ Reward r = mEnabledEconomicPolicies.valueAt(i).getReward(rewardName);
+ if (r != null) {
+ exists = true;
+ instantReward += r.instantReward;
+ ongoingReward += r.ongoingRewardPerSecond;
+ maxReward += r.maxDailyReward;
+ }
+ }
+ final Reward reward = exists
+ ? new Reward(rewardName, instantReward, ongoingReward, maxReward) : null;
+ mRewards.put(rewardName, reward);
+ return reward;
+ }
+
+ @Override
+ void dump(IndentingPrintWriter pw) {
+ dumpActiveModifiers(pw);
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/DeviceIdleModifier.java b/apex/jobscheduler/service/java/com/android/server/tare/DeviceIdleModifier.java
new file mode 100644
index 0000000..20b9c70
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/DeviceIdleModifier.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.tare;
+
+import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.PowerManager;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+
+/** Modifier that makes things more expensive in light and deep doze. */
+class DeviceIdleModifier extends Modifier {
+ private static final String TAG = "TARE-" + DeviceIdleModifier.class.getSimpleName();
+ private static final boolean DEBUG = InternalResourceService.DEBUG
+ || Log.isLoggable(TAG, Log.DEBUG);
+
+ private final InternalResourceService mIrs;
+ private final PowerManager mPowerManager;
+ private final DeviceIdleTracker mDeviceIdleTracker;
+
+ DeviceIdleModifier(@NonNull InternalResourceService irs) {
+ super();
+ mIrs = irs;
+ mPowerManager = irs.getContext().getSystemService(PowerManager.class);
+ mDeviceIdleTracker = new DeviceIdleTracker();
+ mDeviceIdleTracker.startTracking(irs.getContext());
+ }
+
+ @Override
+ long getModifiedCostToProduce(long ctp) {
+ if (mDeviceIdleTracker.mDeviceIdle) {
+ return (long) (1.2 * ctp);
+ }
+ if (mDeviceIdleTracker.mDeviceLightIdle) {
+ return (long) (1.1 * ctp);
+ }
+ return ctp;
+ }
+
+ @Override
+ void dump(IndentingPrintWriter pw) {
+ pw.print("idle=");
+ pw.println(mDeviceIdleTracker.mDeviceIdle);
+ pw.print("lightIdle=");
+ pw.println(mDeviceIdleTracker.mDeviceLightIdle);
+ }
+
+ private final class DeviceIdleTracker extends BroadcastReceiver {
+
+ private volatile boolean mDeviceIdle;
+ private volatile boolean mDeviceLightIdle;
+
+ DeviceIdleTracker() {
+ }
+
+ void startTracking(@NonNull Context context) {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
+ filter.addAction(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED);
+ context.registerReceiver(this, filter);
+
+ // Initialise tracker state.
+ mDeviceIdle = mPowerManager.isDeviceIdleMode();
+ mDeviceLightIdle = mPowerManager.isLightDeviceIdleMode();
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)) {
+ if (mDeviceIdle != mPowerManager.isDeviceIdleMode()) {
+ mDeviceIdle = mPowerManager.isDeviceIdleMode();
+ mIrs.onDeviceStateChanged();
+ }
+ } else if (PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED.equals(action)) {
+ if (mDeviceIdle != mPowerManager.isLightDeviceIdleMode()) {
+ mDeviceLightIdle = mPowerManager.isLightDeviceIdleMode();
+ mIrs.onDeviceStateChanged();
+ }
+ }
+ }
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
new file mode 100644
index 0000000..f9effa5
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.tare;
+
+import static com.android.server.tare.Modifier.COST_MODIFIER_CHARGING;
+import static com.android.server.tare.Modifier.COST_MODIFIER_DEVICE_IDLE;
+import static com.android.server.tare.Modifier.COST_MODIFIER_POWER_SAVE_MODE;
+import static com.android.server.tare.Modifier.COST_MODIFIER_PROCESS_STATE;
+import static com.android.server.tare.Modifier.NUM_COST_MODIFIERS;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringDef;
+import android.util.IndentingPrintWriter;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * An EconomicPolicy includes pricing information and daily ARC requirements and suggestions.
+ * Policies are defined per participating system service. This allows each service’s EconomicPolicy
+ * to be isolated while allowing the core economic system to scale across policies to achieve a
+ * logical system-wide value system.
+ */
+public abstract class EconomicPolicy {
+ private static final String TAG = "TARE-" + EconomicPolicy.class.getSimpleName();
+
+ // fixme ensure actions and rewards never use the same strings
+ static final String REWARD_TOP_ACTIVITY = "REWARD_TOP_ACTIVITY";
+ static final String REWARD_NOTIFICATION_SEEN = "REWARD_NOTIFICATION_SEEN";
+ static final String REWARD_NOTIFICATION_INTERACTION = "REWARD_NOTIFICATION_INTERACTION";
+ static final String REWARD_WIDGET_INTERACTION = "REWARD_WIDGET_INTERACTION";
+ static final String REWARD_OTHER_USER_INTERACTION = "REWARD_OTHER_USER_INTERACTION";
+
+ @StringDef({
+ REWARD_TOP_ACTIVITY,
+ REWARD_NOTIFICATION_SEEN,
+ REWARD_NOTIFICATION_INTERACTION,
+ REWARD_WIDGET_INTERACTION,
+ REWARD_OTHER_USER_INTERACTION,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface UtilityReward {
+ }
+
+ static class Action {
+ @NonNull
+ public final String name;
+ /**
+ * How many ARCs the system says it takes to perform this action.
+ */
+ public final long costToProduce;
+ /**
+ * The base price to perform this action. If this is
+ * less than the {@link #costToProduce}, then the system should not perform
+ * the action unless a modifier lowers the cost to produce.
+ */
+ public final long basePrice;
+
+ Action(@NonNull String name, long costToProduce, long basePrice) {
+ this.name = name;
+ this.costToProduce = costToProduce;
+ this.basePrice = basePrice;
+ }
+ }
+
+ static class Reward {
+ @NonNull
+ @UtilityReward
+ public final String name;
+ public final long instantReward;
+ /** Reward credited per second of ongoing activity. */
+ public final long ongoingRewardPerSecond;
+ /** The maximum amount an app can earn from this reward within a 24 hour period. */
+ public final long maxDailyReward;
+
+ Reward(@NonNull String name, long instantReward, long ongoingReward, long maxDailyReward) {
+ this.name = name;
+ this.instantReward = instantReward;
+ this.ongoingRewardPerSecond = ongoingReward;
+ this.maxDailyReward = maxDailyReward;
+ }
+ }
+
+ private static final Modifier[] COST_MODIFIER_BY_INDEX = new Modifier[NUM_COST_MODIFIERS];
+
+ EconomicPolicy(@NonNull InternalResourceService irs) {
+ for (int mId : getCostModifiers()) {
+ initModifier(mId, irs);
+ }
+ }
+
+ @CallSuper
+ void onSystemServicesReady() {
+ for (int i = 0; i < NUM_COST_MODIFIERS; ++i) {
+ final Modifier modifier = COST_MODIFIER_BY_INDEX[i];
+ if (modifier != null) {
+ modifier.onSystemServicesReady();
+ }
+ }
+ }
+
+ /**
+ * Returns the minimum suggested balance an app should have when the device is at 100% battery.
+ * This takes into account any exemptions the app may have.
+ */
+ abstract long getMinSatiatedBalance(int userId, @NonNull String pkgName);
+
+ /**
+ * Returns the maximum balance an app should have when the device is at 100% battery. This
+ * exists to ensure that no single app accumulate all available resources and increases fairness
+ * for all apps.
+ */
+ abstract long getMaxSatiatedBalance();
+
+ /**
+ * Returns the maximum number of narcs that should be in circulation at once when the device is
+ * at 100% battery.
+ */
+ abstract long getMaxSatiatedCirculation();
+
+ /** Return the set of modifiers that should apply to this policy's costs. */
+ @NonNull
+ abstract int[] getCostModifiers();
+
+ @Nullable
+ abstract Action getAction(@NonNull String actionName);
+
+ @Nullable
+ abstract Reward getReward(@NonNull String rewardName);
+
+ void dump(IndentingPrintWriter pw) {
+ }
+
+ final long getCostOfAction(@NonNull String actionName, int userId, @NonNull String pkgName) {
+ final Action action = getAction(actionName);
+ if (action == null) {
+ return 0;
+ }
+ long ctp = action.costToProduce;
+ long price = action.basePrice;
+ final int[] costModifiers = getCostModifiers();
+ boolean useProcessStatePriceDeterminant = false;
+ for (int costModifier : costModifiers) {
+ if (costModifier == COST_MODIFIER_PROCESS_STATE) {
+ useProcessStatePriceDeterminant = true;
+ } else {
+ final Modifier modifier = getModifier(costModifier);
+ ctp = modifier.getModifiedCostToProduce(ctp);
+ price = modifier.getModifiedPrice(price);
+ }
+ }
+ // ProcessStateModifier needs to be done last.
+ if (useProcessStatePriceDeterminant) {
+ ProcessStateModifier processStateModifier =
+ (ProcessStateModifier) getModifier(COST_MODIFIER_PROCESS_STATE);
+ price = processStateModifier.getModifiedPrice(userId, pkgName, ctp, price);
+ }
+ return price;
+ }
+
+ private static void initModifier(@Modifier.CostModifier final int modifierId,
+ @NonNull InternalResourceService irs) {
+ if (modifierId < 0 || modifierId >= COST_MODIFIER_BY_INDEX.length) {
+ throw new IllegalArgumentException("Invalid modifier id " + modifierId);
+ }
+ Modifier modifier = COST_MODIFIER_BY_INDEX[modifierId];
+ if (modifier == null) {
+ switch (modifierId) {
+ case COST_MODIFIER_CHARGING:
+ modifier = new ChargingModifier(irs);
+ break;
+ case COST_MODIFIER_DEVICE_IDLE:
+ modifier = new DeviceIdleModifier(irs);
+ break;
+ case COST_MODIFIER_POWER_SAVE_MODE:
+ modifier = new PowerSaveModeModifier(irs);
+ break;
+ case COST_MODIFIER_PROCESS_STATE:
+ modifier = new ProcessStateModifier(irs);
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid modifier id " + modifierId);
+ }
+ COST_MODIFIER_BY_INDEX[modifierId] = modifier;
+ }
+ }
+
+ @NonNull
+ private static Modifier getModifier(@Modifier.CostModifier final int modifierId) {
+ if (modifierId < 0 || modifierId >= COST_MODIFIER_BY_INDEX.length) {
+ throw new IllegalArgumentException("Invalid modifier id " + modifierId);
+ }
+ final Modifier modifier = COST_MODIFIER_BY_INDEX[modifierId];
+ if (modifier == null) {
+ throw new IllegalStateException(
+ "Modifier #" + modifierId + " was never initialized");
+ }
+ return modifier;
+ }
+
+ protected static void dumpActiveModifiers(IndentingPrintWriter pw) {
+ for (int i = 0; i < NUM_COST_MODIFIERS; ++i) {
+ pw.print("Modifier ");
+ pw.println(i);
+ pw.increaseIndent();
+
+ Modifier modifier = COST_MODIFIER_BY_INDEX[i];
+ if (modifier != null) {
+ modifier.dump(pw);
+ } else {
+ pw.println("NOT ACTIVE");
+ }
+
+ pw.decreaseIndent();
+ }
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java
new file mode 100644
index 0000000..688ad03
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.tare;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+/**
+ * Interface for the system server to deal with the resource economy subsystem.
+ *
+ * @hide
+ */
+interface EconomyManagerInternal {
+ /** Listener for when an app changes its solvency status. */
+ interface BalanceChangeListener {
+ /**
+ * Called when an app runs out of funds.
+ * {@link #noteOngoingEventStopped(int, String, String, String)} must still be called to formally
+ * end the action.
+ */
+ void onBankruptcy(int userId, @NonNull String pkgName);
+
+ /**
+ * Called when an app goes from being insolvent to solvent.
+ */
+ void onSolvent(int userId, @NonNull String pkgName);
+ }
+
+ /** Register a {@link BalanceChangeListener} to track all apps' solvency status changes. */
+ void registerBalanceChangeListener(@NonNull BalanceChangeListener listener);
+
+ /**
+ * Unregister a {@link BalanceChangeListener} from being notified of any app's solvency status
+ * changes.
+ */
+ void unregisterBalanceChangeListener(@NonNull BalanceChangeListener listener);
+
+ /**
+ * Return {@code true} if the app has a balance equal to or greater than the specified min
+ * balance.
+ */
+ boolean hasBalanceAtLeast(int userId, @NonNull String pkgName, int minBalance);
+
+ /**
+ * Note that an instantaneous event has occurred. The event must be specified in one of the
+ * EconomicPolicies.
+ *
+ * @param tag An optional tag that can be used to differentiate the same event for the same app.
+ */
+ void noteInstantaneousEvent(int userId, @NonNull String pkgName, @NonNull String event,
+ @Nullable String tag);
+
+ /**
+ * Note that a long-running event is starting. The event must be specified in one of the
+ * EconomicPolicies. You must always call
+ * {@link #noteOngoingEventStopped(int, String, String, String)} to end the event. Ongoing
+ * events will be separated and grouped by event-tag combinations. There must be an equal
+ * number of start() and stop() calls for the same event-tag combination in order for the
+ * tracking to finally stop (ie. ongoing events are ref-counted).
+ *
+ * @param tag An optional tag that can be used to differentiate the same event for the same app.
+ */
+ void noteOngoingEventStarted(int userId, @NonNull String pkgName, @NonNull String event,
+ @Nullable String tag);
+
+ /**
+ * Note that a long-running event has stopped. The event must be specified in one of the
+ * EconomicPolicies. Ongoing events are separated and grouped by event-tag combinations.
+ * There must be an equal number of start() and stop() calls for the same event-tag combination
+ * in order for the tracking to finally stop (ie. ongoing events are ref-counted).
+ *
+ * @param tag An optional tag that can be used to differentiate the same event for the same app.
+ */
+ void noteOngoingEventStopped(int userId, @NonNull String pkgName, @NonNull String event,
+ @Nullable String tag);
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
new file mode 100644
index 0000000..390ef81
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.tare;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.BatteryManagerInternal;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseSetArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.tare.EconomyManagerInternal.BalanceChangeListener;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+/**
+ * Responsible for handling app's ARC count based on events, ensuring ARCs are credited when
+ * appropriate, and reclaiming ARCs at the right times. The IRS deals with the high level details
+ * while the {@link Agent} deals with the nitty-gritty details.
+ *
+ * Note on locking: Any function with the suffix 'Locked' needs to lock on {@link #mLock}.
+ *
+ * @hide
+ */
+public class InternalResourceService extends SystemService {
+ public static final String TAG = "TARE-IRS";
+ public static final boolean DEBUG = Log.isLoggable("TARE", Log.DEBUG);
+
+ /** Global local for all resource economy state. */
+ private final Object mLock = new Object();
+
+ private final Handler mHandler;
+ private final BatteryManagerInternal mBatteryManagerInternal;
+ private final PackageManager mPackageManager;
+
+ private final CompleteEconomicPolicy mCompleteEconomicPolicy;
+ private final Agent mAgent;
+
+ private final CopyOnWriteArraySet<BalanceChangeListener> mBalanceChangeListeners =
+ new CopyOnWriteArraySet<>();
+
+ @NonNull
+ @GuardedBy("mLock")
+ private List<PackageInfo> mPkgCache = new ArrayList<>();
+
+ /** Cached mapping of UIDs (for all users) to a list of packages in the UID. */
+ @GuardedBy("mLock")
+ private final SparseSetArray<String> mUidToPackageCache = new SparseSetArray<>();
+
+ @GuardedBy("mLock")
+ private boolean mIsSetup;
+ // In the range [0,100] to represent 0% to 100% battery.
+ @GuardedBy("mLock")
+ private int mCurrentBatteryLevel;
+
+ @SuppressWarnings("FieldCanBeLocal")
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Nullable
+ private String getPackageName(Intent intent) {
+ Uri uri = intent.getData();
+ return uri != null ? uri.getSchemeSpecificPart() : null;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ switch (intent.getAction()) {
+ case Intent.ACTION_BATTERY_LEVEL_CHANGED:
+ onBatteryLevelChanged();
+ break;
+ case Intent.ACTION_PACKAGE_FULLY_REMOVED: {
+ final String pkgName = getPackageName(intent);
+ final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ onPackageRemoved(pkgUid, pkgName);
+ }
+ break;
+ case Intent.ACTION_PACKAGE_ADDED: {
+ if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+ final String pkgName = getPackageName(intent);
+ final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ onPackageAdded(pkgUid, pkgName);
+ }
+ }
+ break;
+ case Intent.ACTION_PACKAGE_RESTARTED: {
+ final String pkgName = getPackageName(intent);
+ final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ final int userId = UserHandle.getUserId(pkgUid);
+ onPackageForceStopped(userId, pkgName);
+ }
+ break;
+ case Intent.ACTION_USER_ADDED: {
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+ onUserAdded(userId);
+ }
+ break;
+ case Intent.ACTION_USER_REMOVED: {
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+ onUserRemoved(userId);
+ }
+ break;
+ }
+ }
+ };
+
+ private static final int MSG_NOTIFY_BALANCE_CHANGE_LISTENERS = 0;
+
+ /**
+ * Initializes the system service.
+ * <p>
+ * Subclasses must define a single argument constructor that accepts the context
+ * and passes it to super.
+ * </p>
+ *
+ * @param context The system server context.
+ */
+ public InternalResourceService(Context context) {
+ super(context);
+
+ mHandler = new IrsHandler(TareHandlerThread.get().getLooper());
+ mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class);
+ mPackageManager = context.getPackageManager();
+ mCompleteEconomicPolicy = new CompleteEconomicPolicy(this);
+ mAgent = new Agent(this, mCompleteEconomicPolicy);
+
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_BATTERY_LEVEL_CHANGED);
+ context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
+ final IntentFilter pkgFilter = new IntentFilter();
+ pkgFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
+ pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
+ pkgFilter.addDataScheme("package");
+ context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, pkgFilter, null, null);
+ final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
+ userFilter.addAction(Intent.ACTION_USER_ADDED);
+ context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
+
+ publishLocalService(EconomyManagerInternal.class, new LocalService());
+ }
+
+ @Override
+ public void onStart() {
+
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (PHASE_SYSTEM_SERVICES_READY == phase) {
+ synchronized (mLock) {
+ mCurrentBatteryLevel = getCurrentBatteryLevel();
+ // TODO: base on if we have anything persisted
+ final boolean isFirstSetup = true;
+ if (isFirstSetup) {
+ mHandler.post(this::setupEconomy);
+ } else {
+ mIsSetup = true;
+ }
+ }
+ }
+ }
+
+ @NonNull
+ Object getLock() {
+ return mLock;
+ }
+
+ @NonNull
+ List<PackageInfo> getInstalledPackages() {
+ synchronized (mLock) {
+ return mPkgCache;
+ }
+ }
+
+ @GuardedBy("mLock")
+ long getMaxCirculationLocked() {
+ return mCurrentBatteryLevel * mCompleteEconomicPolicy.getMaxSatiatedCirculation() / 100;
+ }
+
+ @GuardedBy("mLock")
+ long getMinBalanceLocked(final int userId, @NonNull final String pkgName) {
+ return mCurrentBatteryLevel * mCompleteEconomicPolicy.getMinSatiatedBalance(userId, pkgName)
+ / 100;
+ }
+
+ @Nullable
+ @GuardedBy("mLock")
+ ArraySet<String> getPackagesForUidLocked(final int uid) {
+ ArraySet<String> packages = mUidToPackageCache.get(uid);
+ if (packages == null) {
+ final String[] pkgs = mPackageManager.getPackagesForUid(uid);
+ if (pkgs != null) {
+ for (String pkg : pkgs) {
+ mUidToPackageCache.add(uid, pkg);
+ }
+ packages = mUidToPackageCache.get(uid);
+ }
+ }
+ return packages;
+ }
+
+ void onBatteryLevelChanged() {
+ synchronized (mLock) {
+ final int newBatteryLevel = getCurrentBatteryLevel();
+ if (newBatteryLevel > mCurrentBatteryLevel) {
+ mAgent.distributeBasicIncomeLocked(mCurrentBatteryLevel);
+ }
+ mCurrentBatteryLevel = newBatteryLevel;
+ }
+ }
+
+ void onDeviceStateChanged() {
+ }
+
+ void onPackageAdded(final int uid, @NonNull final String pkgName) {
+ final int userId = UserHandle.getUserId(uid);
+ final PackageInfo packageInfo;
+ try {
+ packageInfo = mPackageManager.getPackageInfoAsUser(pkgName, 0, userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.wtf(TAG, "PM couldn't find newly added package: " + pkgName);
+ return;
+ }
+ synchronized (mLock) {
+ mPkgCache.add(packageInfo);
+ mUidToPackageCache.add(uid, pkgName);
+ // TODO: only do this when the user first launches the app (app leaves stopped state)
+ mAgent.grantBirthrightLocked(userId, pkgName);
+ }
+ }
+
+ void onPackageForceStopped(final int userId, @NonNull final String pkgName) {
+ synchronized (mLock) {
+ // TODO: reduce ARC count by some amount
+ }
+ }
+
+ void onPackageRemoved(final int uid, @NonNull final String pkgName) {
+ final int userId = UserHandle.getUserId(uid);
+ synchronized (mLock) {
+ mUidToPackageCache.remove(uid, pkgName);
+ for (int i = 0; i < mPkgCache.size(); ++i) {
+ PackageInfo pkgInfo = mPkgCache.get(i);
+ if (UserHandle.getUserId(pkgInfo.applicationInfo.uid) == userId
+ && pkgName.equals(pkgInfo.packageName)) {
+ mPkgCache.remove(i);
+ break;
+ }
+ }
+ mAgent.onPackageRemovedLocked(userId, pkgName);
+ }
+ }
+
+ void onUidStateChanged(final int uid) {
+ }
+
+ void onUserAdded(final int userId) {
+ synchronized (mLock) {
+ loadInstalledPackageListLocked();
+ mAgent.grantBirthrightsLocked(userId);
+ }
+ }
+
+ void onUserRemoved(final int userId) {
+ synchronized (mLock) {
+ 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) {
+ removedPkgs.add(pkgInfo.packageName);
+ mUidToPackageCache.remove(pkgInfo.applicationInfo.uid);
+ mPkgCache.remove(i);
+ break;
+ }
+ }
+ loadInstalledPackageListLocked();
+ mAgent.onUserRemovedLocked(userId, removedPkgs);
+ }
+ }
+
+ void postSolvencyChanged(final int userId, @NonNull final String pkgName, boolean nowSolvent) {
+ mHandler.obtainMessage(
+ MSG_NOTIFY_BALANCE_CHANGE_LISTENERS, userId, nowSolvent ? 1 : 0, pkgName)
+ .sendToTarget();
+ }
+
+ private int getCurrentBatteryLevel() {
+ return mBatteryManagerInternal.getBatteryLevel();
+ }
+
+ @GuardedBy("mLock")
+ private void loadInstalledPackageListLocked() {
+ mPkgCache = mPackageManager.getInstalledPackages(0);
+ }
+
+ private void setupEconomy() {
+ synchronized (mLock) {
+ loadInstalledPackageListLocked();
+ mAgent.grantBirthrightsLocked();
+ mIsSetup = true;
+ }
+ }
+
+ private final class IrsHandler extends Handler {
+ IrsHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_NOTIFY_BALANCE_CHANGE_LISTENERS:
+ final int userId = msg.arg1;
+ final String pkgName = (String) msg.obj;
+ final boolean nowSolvent = msg.arg2 == 1;
+ for (BalanceChangeListener listener : mBalanceChangeListeners) {
+ if (nowSolvent) {
+ listener.onSolvent(userId, pkgName);
+ } else {
+ listener.onBankruptcy(userId, pkgName);
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ // TODO: implement
+ private final class LocalService implements EconomyManagerInternal {
+
+ @Override
+ public void registerBalanceChangeListener(@NonNull BalanceChangeListener listener) {
+ mBalanceChangeListeners.add(listener);
+ }
+
+ @Override
+ public void unregisterBalanceChangeListener(@NonNull BalanceChangeListener listener) {
+ mBalanceChangeListeners.remove(listener);
+ }
+
+ @Override
+ public boolean hasBalanceAtLeast(int userId, @NonNull String pkgName, int minBalance) {
+ return false;
+ }
+
+ @Override
+ public void noteInstantaneousEvent(int userId, @NonNull String pkgName,
+ @NonNull String event, @Nullable String tag) {
+ }
+
+ @Override
+ public void noteOngoingEventStarted(int userId, @NonNull String pkgName,
+ @NonNull String event, @Nullable String tag) {
+ }
+
+ @Override
+ public void noteOngoingEventStopped(int userId, @NonNull String pkgName,
+ @NonNull String event, @Nullable String tag) {
+ }
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
new file mode 100644
index 0000000..4a2136b
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.tare;
+
+import static com.android.server.tare.Modifier.COST_MODIFIER_CHARGING;
+import static com.android.server.tare.Modifier.COST_MODIFIER_POWER_SAVE_MODE;
+import static com.android.server.tare.Modifier.COST_MODIFIER_PROCESS_STATE;
+import static com.android.server.tare.TareUtils.arcToNarc;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.ArrayMap;
+
+/**
+ * Policy defining pricing information and daily ARC requirements and suggestions for
+ * JobScheduler.
+ */
+public class JobSchedulerEconomicPolicy extends EconomicPolicy {
+ public static final String ACTION_JOB_MAX_START = "JOB_MAX_START";
+ public static final String ACTION_JOB_MAX_RUNNING = "JOB_MAX_RUNNING";
+ public static final String ACTION_JOB_HIGH_START = "JOB_HIGH_START";
+ public static final String ACTION_JOB_HIGH_RUNNING = "JOB_HIGH_RUNNING";
+ public static final String ACTION_JOB_DEFAULT_START = "JOB_DEFAULT_START";
+ public static final String ACTION_JOB_DEFAULT_RUNNING = "JOB_DEFAULT_RUNNING";
+ public static final String ACTION_JOB_LOW_START = "JOB_LOW_START";
+ public static final String ACTION_JOB_LOW_RUNNING = "JOB_LOW_RUNNING";
+ public static final String ACTION_JOB_MIN_START = "JOB_MIN_START";
+ public static final String ACTION_JOB_MIN_RUNNING = "JOB_MIN_RUNNING";
+ public static final String ACTION_JOB_TIMEOUT = "JOB_TIMEOUT";
+
+ private static final int[] COST_MODIFIERS = new int[]{
+ COST_MODIFIER_CHARGING,
+ COST_MODIFIER_POWER_SAVE_MODE,
+ COST_MODIFIER_PROCESS_STATE
+ };
+
+ private final ArrayMap<String, Action> mActions = new ArrayMap<>();
+ private final ArrayMap<String, Reward> mRewards = new ArrayMap<>();
+
+ JobSchedulerEconomicPolicy(InternalResourceService irs) {
+ super(irs);
+ loadActions();
+ loadRewards();
+ }
+
+ @Override
+ long getMinSatiatedBalance(final int userId, @NonNull final String pkgName) {
+ // TODO: incorporate time since usage
+ return arcToNarc(2000);
+ }
+
+ @Override
+ long getMaxSatiatedBalance() {
+ return arcToNarc(60000);
+ }
+
+ @Override
+ long getMaxSatiatedCirculation() {
+ return arcToNarc(691200);
+ }
+
+ @NonNull
+ @Override
+ int[] getCostModifiers() {
+ return COST_MODIFIERS;
+ }
+
+ @Nullable
+ @Override
+ Action getAction(@NonNull String actionName) {
+ return mActions.get(actionName);
+ }
+
+ @Nullable
+ @Override
+ Reward getReward(@NonNull String rewardName) {
+ return mRewards.get(rewardName);
+ }
+
+ private void loadActions() {
+ mActions.put(ACTION_JOB_MAX_START,
+ new Action(ACTION_JOB_MAX_START, arcToNarc(3), arcToNarc(10)));
+ mActions.put(ACTION_JOB_MAX_RUNNING,
+ new Action(ACTION_JOB_MAX_RUNNING, arcToNarc(2), arcToNarc(5)));
+ mActions.put(ACTION_JOB_HIGH_START,
+ new Action(ACTION_JOB_HIGH_START, arcToNarc(3), arcToNarc(8)));
+ mActions.put(ACTION_JOB_HIGH_RUNNING,
+ new Action(ACTION_JOB_HIGH_RUNNING, arcToNarc(2), arcToNarc(4)));
+ mActions.put(ACTION_JOB_DEFAULT_START,
+ new Action(ACTION_JOB_DEFAULT_START, arcToNarc(3), arcToNarc(6)));
+ mActions.put(ACTION_JOB_DEFAULT_RUNNING,
+ new Action(ACTION_JOB_DEFAULT_RUNNING, arcToNarc(2), arcToNarc(3)));
+ mActions.put(ACTION_JOB_LOW_START,
+ new Action(ACTION_JOB_LOW_START, arcToNarc(3), arcToNarc(4)));
+ mActions.put(ACTION_JOB_LOW_RUNNING,
+ new Action(ACTION_JOB_LOW_RUNNING, arcToNarc(2), arcToNarc(2)));
+ mActions.put(ACTION_JOB_MIN_START,
+ new Action(ACTION_JOB_MIN_START, arcToNarc(3), arcToNarc(2)));
+ mActions.put(ACTION_JOB_MIN_RUNNING,
+ new Action(ACTION_JOB_MIN_RUNNING, arcToNarc(2), arcToNarc(1)));
+ mActions.put(ACTION_JOB_TIMEOUT,
+ new Action(ACTION_JOB_TIMEOUT, arcToNarc(30), arcToNarc(60)));
+ }
+
+ private void loadRewards() {
+ mRewards.put(REWARD_TOP_ACTIVITY,
+ new Reward(REWARD_TOP_ACTIVITY,
+ arcToNarc(0), /* .5 arcs */ arcToNarc(5) / 10, arcToNarc(15000)));
+ mRewards.put(REWARD_NOTIFICATION_SEEN,
+ new Reward(REWARD_NOTIFICATION_SEEN, arcToNarc(1), arcToNarc(0), arcToNarc(10)));
+ mRewards.put(REWARD_NOTIFICATION_INTERACTION,
+ new Reward(REWARD_NOTIFICATION_INTERACTION,
+ arcToNarc(5), arcToNarc(0), arcToNarc(5000)));
+ mRewards.put(REWARD_WIDGET_INTERACTION,
+ new Reward(REWARD_WIDGET_INTERACTION,
+ arcToNarc(10), arcToNarc(0), arcToNarc(5000)));
+ mRewards.put(REWARD_OTHER_USER_INTERACTION,
+ new Reward(REWARD_OTHER_USER_INTERACTION,
+ arcToNarc(10), arcToNarc(0), arcToNarc(5000)));
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java b/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
new file mode 100644
index 0000000..e2fd8a4
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.tare;
+
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+
+import static com.android.server.tare.TareUtils.narcToArc;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.ArrayMap;
+import android.util.IndentingPrintWriter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Ledger to track the last recorded balance and recent activities of an app.
+ */
+class Ledger {
+ static class Transaction {
+ public final long startTimeMs;
+ public final long endTimeMs;
+ @NonNull
+ public final String reason;
+ @Nullable
+ public final String tag;
+ public final long delta;
+
+ Transaction(long startTimeMs, long endTimeMs,
+ @NonNull String reason, @Nullable String tag, long delta) {
+ this.startTimeMs = startTimeMs;
+ this.endTimeMs = endTimeMs;
+ this.reason = reason;
+ this.tag = tag;
+ this.delta = delta;
+ }
+ }
+
+ /** Last saved balance. This doesn't take currently ongoing events into account. */
+ private long mCurrentBalance = 0;
+ private final List<Transaction> mTransactions = new ArrayList<>();
+ private final ArrayMap<String, Long> mCumulativeDeltaPerReason = new ArrayMap<>();
+ private long mEarliestSumTime;
+
+ Ledger() {
+ }
+
+ long getCurrentBalance() {
+ return mCurrentBalance;
+ }
+
+ void recordTransaction(@NonNull Transaction transaction) {
+ mTransactions.add(transaction);
+ mCurrentBalance += transaction.delta;
+
+ Long sum = mCumulativeDeltaPerReason.get(transaction.reason);
+ if (sum == null) {
+ sum = 0L;
+ }
+ sum += transaction.delta;
+ mCumulativeDeltaPerReason.put(transaction.reason, sum);
+ mEarliestSumTime = Math.min(mEarliestSumTime, transaction.startTimeMs);
+ }
+
+ long get24HourSum(@NonNull String reason, final long now) {
+ final long windowStartTime = now - 24 * HOUR_IN_MILLIS;
+ if (mEarliestSumTime < windowStartTime) {
+ // Need to redo sums
+ mCumulativeDeltaPerReason.clear();
+ for (int i = mTransactions.size() - 1; i >= 0; --i) {
+ final Transaction transaction = mTransactions.get(i);
+ if (transaction.endTimeMs <= windowStartTime) {
+ break;
+ }
+ final Long sumObj = mCumulativeDeltaPerReason.get(transaction.reason);
+ long sum = sumObj == null ? 0 : sumObj;
+ if (transaction.startTimeMs >= windowStartTime) {
+ sum += transaction.delta;
+ } else {
+ // Pro-rate durationed deltas. Intentionally floor the result.
+ sum += (long) (1.0 * (transaction.endTimeMs - windowStartTime)
+ * transaction.delta)
+ / (transaction.endTimeMs - transaction.startTimeMs);
+ }
+ mCumulativeDeltaPerReason.put(transaction.reason, sum);
+ }
+ mEarliestSumTime = windowStartTime;
+ }
+ Long sum = mCumulativeDeltaPerReason.get(reason);
+ if (sum == null) {
+ return 0;
+ }
+ return sum;
+ }
+
+ void dump(IndentingPrintWriter pw) {
+ pw.println("Ledger{");
+ pw.increaseIndent();
+
+ pw.print("cur balance", narcToArc(getCurrentBalance())).println();
+
+ pw.decreaseIndent();
+ pw.println("}");
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Modifier.java b/apex/jobscheduler/service/java/com/android/server/tare/Modifier.java
new file mode 100644
index 0000000..6b89b79
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Modifier.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.tare;
+
+import android.annotation.IntDef;
+import android.util.IndentingPrintWriter;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Base class of a modifier that can affect end pricing.
+ */
+abstract class Modifier {
+ static final int COST_MODIFIER_CHARGING = 0;
+ static final int COST_MODIFIER_DEVICE_IDLE = 1;
+ static final int COST_MODIFIER_POWER_SAVE_MODE = 2;
+ static final int COST_MODIFIER_PROCESS_STATE = 3;
+ static final int NUM_COST_MODIFIERS = COST_MODIFIER_PROCESS_STATE + 1;
+
+ @IntDef({
+ COST_MODIFIER_CHARGING,
+ COST_MODIFIER_DEVICE_IDLE,
+ COST_MODIFIER_POWER_SAVE_MODE,
+ COST_MODIFIER_PROCESS_STATE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CostModifier {
+ }
+
+ /**
+ * Returns a modified cost to produce based on the modifier's state.
+ *
+ * @param ctp Current cost to produce
+ */
+ long getModifiedCostToProduce(long ctp) {
+ return ctp;
+ }
+
+ /**
+ * Returns a modified price based on the modifier's state.
+ *
+ * @param price Current price
+ */
+ long getModifiedPrice(long price) {
+ return price;
+ }
+
+ void onSystemServicesReady() {
+ }
+
+ abstract void dump(IndentingPrintWriter pw);
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/OWNERS b/apex/jobscheduler/service/java/com/android/server/tare/OWNERS
new file mode 100644
index 0000000..96ec75f
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/OWNERS
@@ -0,0 +1,5 @@
+dplotnikov@google.com
+kwekua@google.com
+mwachens@google.com
+suprabh@google.com
+yamasani@google.com
\ No newline at end of file
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/PowerSaveModeModifier.java b/apex/jobscheduler/service/java/com/android/server/tare/PowerSaveModeModifier.java
new file mode 100644
index 0000000..764a3a8
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/PowerSaveModeModifier.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.tare;
+
+import android.annotation.NonNull;
+import android.os.PowerManager;
+import android.util.IndentingPrintWriter;
+
+/** Modifier that makes things more expensive in adaptive and full battery saver are active. */
+class PowerSaveModeModifier extends Modifier {
+ private final InternalResourceService mIrs;
+ private final PowerManager mPowerManager;
+
+ PowerSaveModeModifier(@NonNull InternalResourceService irs) {
+ super();
+ mIrs = irs;
+ mPowerManager = irs.getContext().getSystemService(PowerManager.class);
+ }
+
+ @Override
+ long getModifiedCostToProduce(long ctp) {
+ if (mPowerManager.isPowerSaveMode()) {
+ return (long) (1.5 * ctp);
+ }
+ // TODO: get adaptive power save mode
+ if (mPowerManager.isPowerSaveMode()) {
+ return (long) (1.25 * ctp);
+ }
+ return ctp;
+ }
+
+ @Override
+ void dump(IndentingPrintWriter pw) {
+ pw.print("power save=");
+ pw.println(mPowerManager.isPowerSaveMode());
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java b/apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java
new file mode 100644
index 0000000..2ee6459
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.tare;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.app.ActivityManager;
+import android.app.IUidObserver;
+import android.content.pm.PackageManagerInternal;
+import android.os.RemoteException;
+import android.util.IndentingPrintWriter;
+import android.util.Slog;
+import android.util.SparseArrayMap;
+import android.util.SparseIntArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.LocalServices;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Modifier that makes things more cheaper based on an app's process state. */
+class ProcessStateModifier extends Modifier {
+ private static final String TAG = "TARE-" + ProcessStateModifier.class.getSimpleName();
+
+ private static final int PROC_STATE_BUCKET_NONE = 0;
+ private static final int PROC_STATE_BUCKET_TOP = 1;
+ private static final int PROC_STATE_BUCKET_FGS = 2;
+ private static final int PROC_STATE_BUCKET_BFGS = 3;
+ private static final int PROC_STATE_BUCKET_BG = 4;
+
+ @IntDef(prefix = {"PROC_STATE_BUCKET_"}, value = {
+ PROC_STATE_BUCKET_NONE,
+ PROC_STATE_BUCKET_TOP,
+ PROC_STATE_BUCKET_FGS,
+ PROC_STATE_BUCKET_BFGS,
+ PROC_STATE_BUCKET_BG
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ProcStateBucket {
+ }
+
+ private final Object mLock = new Object();
+ private final InternalResourceService mIrs;
+ private final PackageManagerInternal mPackageManagerInternal;
+
+ /** Cached mapping of userId+package to their UIDs (for all users) */
+ private final SparseArrayMap<String, Integer> mPackageToUidCache = new SparseArrayMap<>();
+
+ @GuardedBy("mLock")
+ private final SparseIntArray mUidProcStateBucketCache = new SparseIntArray();
+
+ private final IUidObserver mUidObserver = new IUidObserver.Stub() {
+ @Override
+ public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) {
+ final int newBucket = getProcStateBucket(procState);
+ synchronized (mLock) {
+ final int curBucket = mUidProcStateBucketCache.get(uid);
+ if (curBucket != newBucket) {
+ mUidProcStateBucketCache.put(uid, newBucket);
+ }
+ notifyStateChangedLocked(uid);
+ }
+ }
+
+ @Override
+ public void onUidGone(int uid, boolean disabled) {
+ synchronized (mLock) {
+ if (mUidProcStateBucketCache.indexOfKey(uid) < 0) {
+ Slog.e(TAG, "UID " + uid + " marked gone but wasn't in cache.");
+ return;
+ }
+ mUidProcStateBucketCache.delete(uid);
+ notifyStateChangedLocked(uid);
+ }
+ }
+
+ @Override
+ public void onUidActive(int uid) {
+ }
+
+ @Override
+ public void onUidIdle(int uid, boolean disabled) {
+ }
+
+ @Override
+ public void onUidCachedChanged(int uid, boolean cached) {
+ }
+ };
+
+ ProcessStateModifier(@NonNull InternalResourceService irs) {
+ super();
+ mIrs = irs;
+ mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+ }
+
+ @Override
+ @GuardedBy("mLock")
+ void onSystemServicesReady() {
+ try {
+ ActivityManager.getService().registerUidObserver(mUidObserver,
+ ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE,
+ ActivityManager.PROCESS_STATE_UNKNOWN, null);
+ } catch (RemoteException e) {
+ // ignored; both services live in system_server
+ }
+ }
+
+ /**
+ * Get the final modified price based on an app's process state.
+ *
+ * @param ctp Cost to produce. @see EconomicPolicy.Action#costToProduce
+ * @param price Current price
+ */
+ long getModifiedPrice(final int userId, @NonNull final String pkgName,
+ final long ctp, final long price) {
+ final int procState;
+ synchronized (mLock) {
+ procState = mUidProcStateBucketCache.get(
+ getUidLocked(userId, pkgName), PROC_STATE_BUCKET_NONE);
+ }
+ switch (procState) {
+ case PROC_STATE_BUCKET_TOP:
+ return 0;
+ case PROC_STATE_BUCKET_FGS:
+ // Can't get notification priority. Just use CTP for now.
+ return ctp;
+ case PROC_STATE_BUCKET_BFGS:
+ return (long) (ctp + .5 * (price - ctp));
+ case PROC_STATE_BUCKET_BG:
+ default:
+ return price;
+ }
+ }
+
+ @Override
+ @GuardedBy("mLock")
+ void dump(IndentingPrintWriter pw) {
+ pw.print("Proc state bucket cache = ");
+ pw.println(mUidProcStateBucketCache);
+ }
+
+ @GuardedBy("mLock")
+ private int getUidLocked(final int userId, @NonNull final String pkgName) {
+ if (!mPackageToUidCache.contains(userId, pkgName)) {
+ mPackageToUidCache.add(userId, pkgName,
+ mPackageManagerInternal.getPackageUid(pkgName, 0, userId));
+ }
+ return mPackageToUidCache.get(userId, pkgName);
+ }
+
+ @ProcStateBucket
+ private int getProcStateBucket(int procState) {
+ if (procState <= ActivityManager.PROCESS_STATE_TOP) {
+ return PROC_STATE_BUCKET_TOP;
+ }
+ if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
+ return PROC_STATE_BUCKET_FGS;
+ }
+ if (procState <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
+ return PROC_STATE_BUCKET_BFGS;
+ }
+ return PROC_STATE_BUCKET_BG;
+ }
+
+ @GuardedBy("mLock")
+ private void notifyStateChangedLocked(final int uid) {
+ // Never call out to the IRS with the local lock held.
+ TareHandlerThread.getHandler().post(() -> mIrs.onUidStateChanged(uid));
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/tare/TEST_MAPPING
new file mode 100644
index 0000000..73b00b6
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/TEST_MAPPING
@@ -0,0 +1,34 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksMockingServicesTests",
+ "options": [
+ {"include-filter": "com.android.server.tare"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"}
+ ]
+ },
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {"include-filter": "com.android.server.tare"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"}
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "FrameworksMockingServicesTests",
+ "options": [
+ {"include-filter": "com.android.server.tare"}
+ ]
+ },
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {"include-filter": "com.android.server.tare"}
+ ]
+ }
+ ]
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/TareHandlerThread.java b/apex/jobscheduler/service/java/com/android/server/tare/TareHandlerThread.java
new file mode 100644
index 0000000..5b2b660
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/TareHandlerThread.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.tare;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Trace;
+
+/**
+ * Singleton thread for all of TARE.
+ *
+ * @see com.android.internal.os.BackgroundThread
+ */
+final class TareHandlerThread extends HandlerThread {
+
+ private static TareHandlerThread sInstance;
+ private static Handler sHandler;
+
+ private TareHandlerThread() {
+ super("tare");
+ }
+
+ private static void ensureThreadLocked() {
+ if (sInstance == null) {
+ sInstance = new TareHandlerThread();
+ sInstance.start();
+ final Looper looper = sInstance.getLooper();
+ looper.setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
+ sHandler = new Handler(sInstance.getLooper());
+ }
+ }
+
+ static TareHandlerThread get() {
+ synchronized (TareHandlerThread.class) {
+ ensureThreadLocked();
+ }
+ return sInstance;
+ }
+
+ /** Returns the singleton handler for TareHandlerThread. */
+ public static Handler getHandler() {
+ synchronized (TareHandlerThread.class) {
+ ensureThreadLocked();
+ }
+ return sHandler;
+ }
+}
diff --git a/media/aidl/android/media/soundtrigger_middleware/ModelParameterRange.aidl b/apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java
similarity index 64%
copy from media/aidl/android/media/soundtrigger_middleware/ModelParameterRange.aidl
copy to apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java
index d6948a8..b3d1a0c 100644
--- a/media/aidl/android/media/soundtrigger_middleware/ModelParameterRange.aidl
+++ b/apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,16 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
-/**
- * Value range for a model parameter.
- *
- * {@hide}
- */
-parcelable ModelParameterRange {
- /** Minimum (inclusive) */
- int minInclusive;
- /** Maximum (inclusive) */
- int maxInclusive;
+package com.android.server.tare;
+
+class TareUtils {
+ static long arcToNarc(int arcs) {
+ return arcs * 1_000_000_000L;
+ }
+
+ static int narcToArc(long narcs) {
+ return (int) (narcs / 1_000_000_000);
+ }
}
diff --git a/apex/media/framework/TEST_MAPPING b/apex/media/framework/TEST_MAPPING
index 70c9087..3d21914 100644
--- a/apex/media/framework/TEST_MAPPING
+++ b/apex/media/framework/TEST_MAPPING
@@ -4,7 +4,7 @@
"name": "CtsMediaParserTestCases"
},
{
- "name": "CtsMediaParserHostSideTestCases"
+ "name": "CtsMediaParserHostTestCases"
}
]
}
diff --git a/api/Android.bp b/api/Android.bp
index db1f64c..a84e6a6 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -69,7 +69,10 @@
dest: "current.txt",
},
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/public/api",
dest: "android.txt",
},
@@ -151,7 +154,10 @@
dest: "removed.txt",
},
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/public/api",
dest: "removed.txt",
},
@@ -187,7 +193,10 @@
dest: "system-current.txt",
},
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/system/api",
dest: "android.txt",
},
@@ -242,7 +251,10 @@
dest: "system-removed.txt",
},
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/system/api",
dest: "removed.txt",
},
@@ -279,7 +291,10 @@
dest: "module-lib-current.txt",
},
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/module-lib/api",
dest: "android.txt",
},
@@ -336,7 +351,10 @@
dest: "module-lib-removed.txt",
},
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/module-lib/api",
dest: "removed.txt",
},
@@ -377,7 +395,10 @@
dest: "system-server-current.txt",
},
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/system-server/api",
dest: "android.txt",
},
@@ -401,7 +422,10 @@
dest: "system-server-removed.txt",
},
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/system-server/api",
dest: "removed.txt",
},
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index d4da5e5..7e6a521 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -45,13 +45,13 @@
#define COLORSPACE_SRGB 1
#define COLORSPACE_DISPLAY_P3 2
-static void usage(const char* pname, PhysicalDisplayId displayId)
+static void usage(const char* pname, DisplayId displayId)
{
fprintf(stderr,
"usage: %s [-hp] [-d display-id] [FILENAME]\n"
" -h: this message\n"
" -p: save the file as a png.\n"
- " -d: specify the physical display ID to capture (default: %s)\n"
+ " -d: specify the display ID to capture (default: %s)\n"
" see \"dumpsys SurfaceFlinger --display-id\" for valid display IDs.\n"
"If FILENAME ends with .png it will be saved as a png.\n"
"If FILENAME is not given, the results will be printed to stdout.\n",
@@ -121,9 +121,9 @@
int main(int argc, char** argv)
{
- std::optional<PhysicalDisplayId> displayId = SurfaceComposerClient::getInternalDisplayId();
+ std::optional<DisplayId> displayId = SurfaceComposerClient::getInternalDisplayId();
if (!displayId) {
- fprintf(stderr, "Failed to get token for internal display\n");
+ fprintf(stderr, "Failed to get ID for internal display\n");
return 1;
}
@@ -136,7 +136,11 @@
png = true;
break;
case 'd':
- displayId = PhysicalDisplayId(atoll(optarg));
+ displayId = DisplayId::fromValue(atoll(optarg));
+ if (!displayId) {
+ fprintf(stderr, "Invalid display ID\n");
+ return 1;
+ }
break;
case '?':
case 'h':
@@ -182,7 +186,7 @@
ProcessState::self()->startThreadPool();
sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
- status_t result = ScreenshotClient::captureDisplay(displayId->value, captureListener);
+ status_t result = ScreenshotClient::captureDisplay(*displayId, captureListener);
if (result != NO_ERROR) {
close(fd);
return 1;
diff --git a/core/api/current.txt b/core/api/current.txt
index 1de47b5..db66a49 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -9458,6 +9458,7 @@
field public static final int STATE_CONNECTING = 1; // 0x1
field public static final int STATE_DISCONNECTED = 0; // 0x0
field public static final int STATE_DISCONNECTING = 3; // 0x3
+ field public static final int VOLUME_CONTROL = 23; // 0x17
}
public static interface BluetoothProfile.ServiceListener {
@@ -11175,11 +11176,13 @@
field public static final String CATEGORY_APP_CONTACTS = "android.intent.category.APP_CONTACTS";
field public static final String CATEGORY_APP_EMAIL = "android.intent.category.APP_EMAIL";
field public static final String CATEGORY_APP_FILES = "android.intent.category.APP_FILES";
+ field public static final String CATEGORY_APP_FITNESS = "android.intent.category.APP_FITNESS";
field public static final String CATEGORY_APP_GALLERY = "android.intent.category.APP_GALLERY";
field public static final String CATEGORY_APP_MAPS = "android.intent.category.APP_MAPS";
field public static final String CATEGORY_APP_MARKET = "android.intent.category.APP_MARKET";
field public static final String CATEGORY_APP_MESSAGING = "android.intent.category.APP_MESSAGING";
field public static final String CATEGORY_APP_MUSIC = "android.intent.category.APP_MUSIC";
+ field public static final String CATEGORY_APP_WEATHER = "android.intent.category.APP_WEATHER";
field public static final String CATEGORY_BROWSABLE = "android.intent.category.BROWSABLE";
field public static final String CATEGORY_CAR_DOCK = "android.intent.category.CAR_DOCK";
field public static final String CATEGORY_CAR_MODE = "android.intent.category.CAR_MODE";
@@ -12545,6 +12548,7 @@
method public abstract int getInstantAppCookieMaxBytes();
method @NonNull public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @Nullable public abstract android.content.Intent getLaunchIntentForPackage(@NonNull String);
+ method @NonNull public android.content.IntentSender getLaunchIntentSenderForPackage(@NonNull String);
method @Nullable public abstract android.content.Intent getLeanbackLaunchIntentForPackage(@NonNull String);
method @NonNull public java.util.Set<java.lang.String> getMimeGroup(@NonNull String);
method @NonNull public android.content.pm.ModuleInfo getModuleInfo(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -51019,6 +51023,7 @@
method public CharSequence getClassName();
method public CharSequence getContentDescription();
method public int getCurrentItemIndex();
+ method public int getDisplayId();
method public int getFromIndex();
method public int getItemCount();
method public int getMaxScrollX();
@@ -51435,6 +51440,7 @@
public final class AutofillManager {
method public void cancel();
+ method public void clearAutofillRequestCallback();
method public void commit();
method public void disableAutofillServices();
method @Nullable public android.content.ComponentName getAutofillServiceComponentName();
@@ -51460,6 +51466,7 @@
method public void registerCallback(@Nullable android.view.autofill.AutofillManager.AutofillCallback);
method public void requestAutofill(@NonNull android.view.View);
method public void requestAutofill(@NonNull android.view.View, int, @NonNull android.graphics.Rect);
+ method public void setAutofillRequestCallback(@NonNull java.util.concurrent.Executor, @NonNull android.view.autofill.AutofillRequestCallback);
method public void setUserData(@Nullable android.service.autofill.UserData);
method public void unregisterCallback(@Nullable android.view.autofill.AutofillManager.AutofillCallback);
field public static final String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
@@ -51478,6 +51485,10 @@
field public static final int EVENT_INPUT_UNAVAILABLE = 3; // 0x3
}
+ public interface AutofillRequestCallback {
+ method public void onFillRequest(@Nullable android.view.inputmethod.InlineSuggestionsRequest, @NonNull android.os.CancellationSignal, @NonNull android.service.autofill.FillCallback);
+ }
+
public final class AutofillValue implements android.os.Parcelable {
method public int describeContents();
method public static android.view.autofill.AutofillValue forDate(long);
@@ -51848,10 +51859,12 @@
ctor public InlineSuggestionsRequest.Builder(@NonNull java.util.List<android.widget.inline.InlinePresentationSpec>);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder addInlinePresentationSpecs(@NonNull android.widget.inline.InlinePresentationSpec);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest build();
+ method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setClientSupported(boolean);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setExtras(@NonNull android.os.Bundle);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(@NonNull java.util.List<android.widget.inline.InlinePresentationSpec>);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setInlineTooltipPresentationSpec(@NonNull android.widget.inline.InlinePresentationSpec);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setMaxSuggestionCount(int);
+ method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setServiceSupported(boolean);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setSupportedLocales(@NonNull android.os.LocaleList);
}
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index e48a1da..b22a83d 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -266,6 +266,10 @@
field public static final int VPN_UID = 1016; // 0x3f8
}
+ public final class SharedMemory implements java.io.Closeable android.os.Parcelable {
+ method @NonNull public static android.os.SharedMemory create(@NonNull android.os.ParcelFileDescriptor);
+ }
+
public class StatsServiceManager {
method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsCompanionServiceRegisterer();
method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsManagerServiceRegisterer();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 2d73aa67..d33ad4a 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2127,6 +2127,16 @@
field @NonNull public static final android.os.ParcelUuid VOLUME_CONTROL;
}
+ public final class BluetoothVolumeControl implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void close();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) protected void finalize();
+ method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void setVolume(@Nullable android.bluetooth.BluetoothDevice, @IntRange(from=0, to=255) int);
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.volume-control.profile.action.CONNECTION_STATE_CHANGED";
+ }
+
public final class BufferConstraint implements android.os.Parcelable {
ctor public BufferConstraint(int, int, int);
method public int describeContents();
@@ -2848,7 +2858,7 @@
field public static final int PROTECTION_FLAG_APP_PREDICTOR = 2097152; // 0x200000
field public static final int PROTECTION_FLAG_COMPANION = 8388608; // 0x800000
field public static final int PROTECTION_FLAG_CONFIGURATOR = 524288; // 0x80000
- field public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000
+ field @Deprecated public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000
field public static final int PROTECTION_FLAG_INCIDENT_REPORT_APPROVER = 1048576; // 0x100000
field public static final int PROTECTION_FLAG_KNOWN_SIGNER = 134217728; // 0x8000000
field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000
@@ -3297,7 +3307,9 @@
method @Nullable public android.hardware.hdmi.HdmiPlaybackClient getPlaybackClient();
method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public String getPowerControlMode();
method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public String getPowerStateChangeOnActiveSourceLost();
+ method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getRoutingControl();
method @Nullable public android.hardware.hdmi.HdmiSwitchClient getSwitchClient();
+ method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getSystemAudioControl();
method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getSystemAudioModeMuting();
method @Nullable public android.hardware.hdmi.HdmiTvClient getTvClient();
method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getTvSendStandbyOnSleep();
@@ -3313,7 +3325,9 @@
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setHdmiCecVolumeControlEnabled(int);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setPowerControlMode(@NonNull String);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setPowerStateChangeOnActiveSourceLost(@NonNull String);
+ method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setRoutingControl(@NonNull int);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setStandbyMode(boolean);
+ method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setSystemAudioControl(@NonNull int);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setSystemAudioModeMuting(@NonNull int);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setTvSendStandbyOnSleep(@NonNull int);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setTvWakeOnOneTouchPlay(@NonNull int);
@@ -3323,6 +3337,8 @@
field public static final String CEC_SETTING_NAME_HDMI_CEC_VERSION = "hdmi_cec_version";
field public static final String CEC_SETTING_NAME_POWER_CONTROL_MODE = "power_control_mode";
field public static final String CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST = "power_state_change_on_active_source_lost";
+ field public static final String CEC_SETTING_NAME_ROUTING_CONTROL = "routing_control";
+ field public static final String CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL = "system_audio_control";
field public static final String CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING = "system_audio_mode_muting";
field public static final String CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP = "tv_send_standby_on_sleep";
field public static final String CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY = "tv_wake_on_one_touch_play";
@@ -3380,6 +3396,7 @@
field public static final String POWER_CONTROL_MODE_BROADCAST = "broadcast";
field public static final String POWER_CONTROL_MODE_NONE = "none";
field public static final String POWER_CONTROL_MODE_TV = "to_tv";
+ field public static final String POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM = "to_tv_and_audio_system";
field public static final String POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE = "none";
field public static final String POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW = "standby_now";
field public static final int POWER_STATUS_ON = 0; // 0x0
@@ -3395,6 +3412,10 @@
field public static final int RESULT_SUCCESS = 0; // 0x0
field public static final int RESULT_TARGET_NOT_AVAILABLE = 3; // 0x3
field public static final int RESULT_TIMEOUT = 1; // 0x1
+ field public static final int ROUTING_CONTROL_DISABLED = 0; // 0x0
+ field public static final int ROUTING_CONTROL_ENABLED = 1; // 0x1
+ field public static final int SYSTEM_AUDIO_CONTROL_DISABLED = 0; // 0x0
+ field public static final int SYSTEM_AUDIO_CONTROL_ENABLED = 1; // 0x1
field public static final int SYSTEM_AUDIO_MODE_MUTING_DISABLED = 0; // 0x0
field public static final int SYSTEM_AUDIO_MODE_MUTING_ENABLED = 1; // 0x1
field public static final int TIMER_RECORDING_RESULT_EXTRA_CEC_DISABLED = 3; // 0x3
@@ -5388,6 +5409,8 @@
field public static final int MIX_STATE_DISABLED = -1; // 0xffffffff
field public static final int MIX_STATE_IDLE = 0; // 0x0
field public static final int MIX_STATE_MIXING = 1; // 0x1
+ field public static final int MIX_TYPE_PLAYERS = 0; // 0x0
+ field public static final int MIX_TYPE_RECORDERS = 1; // 0x1
field public static final int ROUTE_FLAG_LOOP_BACK = 2; // 0x2
field public static final int ROUTE_FLAG_RENDER = 1; // 0x1
}
@@ -5401,6 +5424,7 @@
}
public class AudioMixingRule {
+ method public int getTargetMixType();
field public static final int RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET = 2; // 0x2
field public static final int RULE_MATCH_ATTRIBUTE_USAGE = 1; // 0x1
field public static final int RULE_MATCH_UID = 4; // 0x4
@@ -5415,6 +5439,7 @@
method public android.media.audiopolicy.AudioMixingRule build();
method public android.media.audiopolicy.AudioMixingRule.Builder excludeMixRule(int, Object) throws java.lang.IllegalArgumentException;
method public android.media.audiopolicy.AudioMixingRule.Builder excludeRule(android.media.AudioAttributes, int) throws java.lang.IllegalArgumentException;
+ method @NonNull public android.media.audiopolicy.AudioMixingRule.Builder setTargetMixType(int);
}
public class AudioPolicy {
@@ -9105,6 +9130,7 @@
field public static final String NAMESPACE_STATSD_NATIVE_BOOT = "statsd_native_boot";
field @Deprecated public static final String NAMESPACE_STORAGE = "storage";
field public static final String NAMESPACE_STORAGE_NATIVE_BOOT = "storage_native_boot";
+ field public static final String NAMESPACE_SWCODEC_NATIVE = "swcodec_native";
field public static final String NAMESPACE_SYSTEMUI = "systemui";
field public static final String NAMESPACE_SYSTEM_TIME = "system_time";
field public static final String NAMESPACE_TELEPHONY = "telephony";
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 5fa75dd..090ac63 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1382,6 +1382,7 @@
package android.media {
public final class AudioAttributes implements android.os.Parcelable {
+ method public static int[] getSdkUsages();
method @NonNull public static String usageToXsdString(int);
method public static int xsdStringToUsage(@NonNull String);
}
@@ -1406,6 +1407,8 @@
method @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public int abandonAudioFocusForTest(@NonNull android.media.AudioFocusRequest, @NonNull String);
method @Nullable public static android.media.AudioDeviceInfo getDeviceInfoFromType(int);
method @IntRange(from=0) @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public long getFadeOutDurationOnFocusLossMillis(@NonNull android.media.AudioAttributes);
+ method public static final int[] getPublicStreamTypes();
+ method public int getStreamMinVolumeInt(int);
method @NonNull public java.util.Map<java.lang.Integer,java.lang.Boolean> getSurroundFormats();
method public boolean hasRegisteredDynamicPolicy();
method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.QUERY_AUDIO_STATE}) public boolean isFullVolumeDevice();
@@ -1430,6 +1433,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS) public static float getMasterBalance();
method public static final int getNumStreamTypes();
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS) public static int setMasterBalance(float);
+ method @NonNull public static String streamToString(int);
field public static final int DEVICE_ROLE_DISABLED = 2; // 0x2
field public static final int DEVICE_ROLE_NONE = 0; // 0x0
field public static final int DEVICE_ROLE_PREFERRED = 1; // 0x1
@@ -1527,6 +1531,13 @@
method @NonNull public android.media.audiopolicy.AudioPolicy.Builder setIsTestFocusPolicy(boolean);
}
+ public final class AudioProductStrategy implements android.os.Parcelable {
+ method @NonNull public static android.media.AudioAttributes getDefaultAttributes();
+ method public int getLegacyStreamTypeForAudioAttributes(@NonNull android.media.AudioAttributes);
+ method public int getVolumeGroupIdForAudioAttributes(@NonNull android.media.AudioAttributes);
+ method public int getVolumeGroupIdForLegacyStreamType(int);
+ }
+
}
package android.media.metrics {
@@ -2846,6 +2857,10 @@
method public long getAccessibilityIdForRegion(@NonNull android.graphics.Region);
}
+ public class AccessibilityRecord {
+ method public void setDisplayId(int);
+ }
+
public final class AccessibilityWindowInfo implements android.os.Parcelable {
method public static void setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger);
}
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index 5409165..e3690e5 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -71,6 +71,10 @@
+AllUpper: android.media.audiopolicy.AudioProductStrategy#sDefaultAttributes:
+ Constant field names must be named with only upper case characters: `android.media.audiopolicy.AudioProductStrategy#sDefaultAttributes`, should be `S_DEFAULT_ATTRIBUTES`?
+
+
ArrayReturn: android.app.UiAutomation#executeShellCommandRw(String):
ArrayReturn: android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel#KeyphraseSoundModel(java.util.UUID, java.util.UUID, byte[], android.hardware.soundtrigger.SoundTrigger.Keyphrase[]) parameter #3:
@@ -537,6 +541,8 @@
+InternalField: android.media.audiopolicy.AudioProductStrategy#sDefaultAttributes:
+ Internal field sDefaultAttributes must not be exposed
InternalField: android.telephony.ims.ImsConferenceState#mParticipants:
@@ -1045,8 +1051,14 @@
MissingNullability: android.location.LocationRequest#writeToParcel(android.os.Parcel, int) parameter #0:
+MissingNullability: android.media.AudioAttributes#SDK_USAGES:
+ Missing nullability on field `SDK_USAGES` in class `class android.media.AudioAttributes`
+MissingNullability: android.media.AudioAttributes#getSdkUsages():
+ Missing nullability on method `getSdkUsages` return
MissingNullability: android.media.AudioFocusInfo#writeToParcel(android.os.Parcel, int) parameter #0:
+MissingNullability: android.media.AudioManager#getPublicStreamTypes():
+ Missing nullability on method `getPublicStreamTypes` return
MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String) parameter #3:
MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String) parameter #4:
@@ -1063,6 +1075,8 @@
MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #6:
+MissingNullability: android.media.AudioSystem#streamToString(int):
+ Missing nullability on method `streamToString` return
MissingNullability: android.media.PlaybackParams#setAudioStretchMode(int):
MissingNullability: android.media.audiofx.AudioEffect#EFFECT_TYPE_NULL:
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index e91209c..45c8dac 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -16,6 +16,8 @@
package android.accessibilityservice;
+import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
+
import android.accessibilityservice.GestureDescription.MotionEventGenerator;
import android.annotation.CallbackExecutor;
import android.annotation.ColorInt;
@@ -27,6 +29,7 @@
import android.app.Service;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.content.ContextWrapper;
import android.content.Intent;
import android.content.pm.ParceledListSlice;
import android.graphics.Bitmap;
@@ -36,6 +39,7 @@
import android.hardware.HardwareBuffer;
import android.hardware.display.DisplayManager;
import android.os.Build;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -102,14 +106,14 @@
* An accessibility is declared as any other service in an AndroidManifest.xml, but it
* must do two things:
* <ul>
- * <ol>
+ * <li>
* Specify that it handles the "android.accessibilityservice.AccessibilityService"
* {@link android.content.Intent}.
- * </ol>
- * <ol>
+ * </li>
+ * <li>
* Request the {@link android.Manifest.permission#BIND_ACCESSIBILITY_SERVICE} permission to
* ensure that only the system can bind to it.
- * </ol>
+ * </li>
* </ul>
* If either of these items is missing, the system will ignore the accessibility service.
* Following is an example declaration:
@@ -961,30 +965,31 @@
}
}
+ @NonNull
@Override
public Context createDisplayContext(Display display) {
- final Context context = super.createDisplayContext(display);
- final int displayId = display.getDisplayId();
- setDefaultTokenInternal(context, displayId);
- return context;
+ return new AccessibilityContext(super.createDisplayContext(display), mConnectionId);
}
- private void setDefaultTokenInternal(Context context, int displayId) {
- final WindowManagerImpl wm = (WindowManagerImpl) context.getSystemService(WINDOW_SERVICE);
- final IAccessibilityServiceConnection connection =
- AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId);
- IBinder token = null;
- if (connection != null) {
- synchronized (mLock) {
- try {
- token = connection.getOverlayWindowToken(displayId);
- } catch (RemoteException re) {
- Log.w(LOG_TAG, "Failed to get window token", re);
- re.rethrowFromSystemServer();
- }
- }
- wm.setDefaultToken(token);
+ @NonNull
+ @Override
+ public Context createWindowContext(int type, @Nullable Bundle options) {
+ final Context context = super.createWindowContext(type, options);
+ if (type != TYPE_ACCESSIBILITY_OVERLAY) {
+ return context;
}
+ return new AccessibilityContext(context, mConnectionId);
+ }
+
+ @NonNull
+ @Override
+ public Context createWindowContext(@NonNull Display display, int type,
+ @Nullable Bundle options) {
+ final Context context = super.createWindowContext(display, type, options);
+ if (type != TYPE_ACCESSIBILITY_OVERLAY) {
+ return context;
+ }
+ return new AccessibilityContext(context, mConnectionId);
}
/**
@@ -2675,4 +2680,58 @@
}
}
}
+
+ private static class AccessibilityContext extends ContextWrapper {
+ private final int mConnectionId;
+
+ private AccessibilityContext(Context base, int connectionId) {
+ super(base);
+ mConnectionId = connectionId;
+ setDefaultTokenInternal(this, getDisplayId());
+ }
+
+ @NonNull
+ @Override
+ public Context createDisplayContext(Display display) {
+ return new AccessibilityContext(super.createDisplayContext(display), mConnectionId);
+ }
+
+ @NonNull
+ @Override
+ public Context createWindowContext(int type, @Nullable Bundle options) {
+ final Context context = super.createWindowContext(type, options);
+ if (type != TYPE_ACCESSIBILITY_OVERLAY) {
+ return context;
+ }
+ return new AccessibilityContext(context, mConnectionId);
+ }
+
+ @NonNull
+ @Override
+ public Context createWindowContext(@NonNull Display display, int type,
+ @Nullable Bundle options) {
+ final Context context = super.createWindowContext(display, type, options);
+ if (type != TYPE_ACCESSIBILITY_OVERLAY) {
+ return context;
+ }
+ return new AccessibilityContext(context, mConnectionId);
+ }
+
+ private void setDefaultTokenInternal(Context context, int displayId) {
+ final WindowManagerImpl wm = (WindowManagerImpl) context.getSystemService(
+ WINDOW_SERVICE);
+ final IAccessibilityServiceConnection connection =
+ AccessibilityInteractionClient.getConnection(mConnectionId);
+ IBinder token = null;
+ if (connection != null) {
+ try {
+ token = connection.getOverlayWindowToken(displayId);
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Failed to get window token", re);
+ re.rethrowFromSystemServer();
+ }
+ wm.setDefaultToken(token);
+ }
+ }
+ }
}
diff --git a/core/java/android/accessibilityservice/AccessibilityTrace.java b/core/java/android/accessibilityservice/AccessibilityTrace.java
new file mode 100644
index 0000000..f28015a
--- /dev/null
+++ b/core/java/android/accessibilityservice/AccessibilityTrace.java
@@ -0,0 +1,216 @@
+/**
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.accessibilityservice;
+
+import java.util.AbstractMap;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Interface to log accessibility trace.
+ *
+ * @hide
+ */
+public interface AccessibilityTrace {
+ String NAME_ACCESSIBILITY_SERVICE_CONNECTION = "IAccessibilityServiceConnection";
+ String NAME_ACCESSIBILITY_SERVICE_CLIENT = "IAccessibilityServiceClient";
+ String NAME_ACCESSIBILITY_MANAGER = "IAccessibilityManager";
+ String NAME_ACCESSIBILITY_MANAGER_CLIENT = "IAccessibilityManagerClient";
+ String NAME_ACCESSIBILITY_INTERACTION_CONNECTION = "IAccessibilityInteractionConnection";
+ String NAME_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK =
+ "IAccessibilityInteractionConnectionCallback";
+ String NAME_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK = "IRemoteMagnificationAnimationCallback";
+ String NAME_WINDOW_MAGNIFICATION_CONNECTION = "IWindowMagnificationConnection";
+ String NAME_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK = "IWindowMagnificationConnectionCallback";
+ String NAME_WINDOW_MANAGER_INTERNAL = "WindowManagerInternal";
+ String NAME_WINDOWS_FOR_ACCESSIBILITY_CALLBACK = "WindowsForAccessibilityCallback";
+ String NAME_MAGNIFICATION_CALLBACK = "MagnificationCallbacks";
+ String NAME_INPUT_FILTER = "InputFilter";
+ String NAME_GESTURE = "Gesture";
+ String NAME_ACCESSIBILITY_SERVICE = "AccessibilityService";
+ String NAME_PACKAGE_BROADCAST_RECEIVER = "PMBroadcastReceiver";
+ String NAME_USER_BROADCAST_RECEIVER = "UserBroadcastReceiver";
+ String NAME_FINGERPRINT = "FingerprintGesture";
+ String NAME_ACCESSIBILITY_INTERACTION_CLIENT = "AccessibilityInteractionClient";
+
+ String NAME_ALL_LOGGINGS = "AllLoggings";
+ String NAME_NONE = "None";
+
+ long FLAGS_ACCESSIBILITY_SERVICE_CONNECTION = 0x0000000000000001L;
+ long FLAGS_ACCESSIBILITY_SERVICE_CLIENT = 0x0000000000000002L;
+ long FLAGS_ACCESSIBILITY_MANAGER = 0x0000000000000004L;
+ long FLAGS_ACCESSIBILITY_MANAGER_CLIENT = 0x0000000000000008L;
+ long FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION = 0x0000000000000010L;
+ long FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK = 0x0000000000000020L;
+ long FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK = 0x0000000000000040L;
+ long FLAGS_WINDOW_MAGNIFICATION_CONNECTION = 0x0000000000000080L;
+ long FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK = 0x0000000000000100L;
+ long FLAGS_WINDOW_MANAGER_INTERNAL = 0x0000000000000200L;
+ long FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK = 0x0000000000000400L;
+ long FLAGS_MAGNIFICATION_CALLBACK = 0x0000000000000800L;
+ long FLAGS_INPUT_FILTER = 0x0000000000001000L;
+ long FLAGS_GESTURE = 0x0000000000002000L;
+ long FLAGS_ACCESSIBILITY_SERVICE = 0x0000000000004000L;
+ long FLAGS_PACKAGE_BROADCAST_RECEIVER = 0x0000000000008000L;
+ long FLAGS_USER_BROADCAST_RECEIVER = 0x0000000000010000L;
+ long FLAGS_FINGERPRINT = 0x0000000000020000L;
+ long FLAGS_ACCESSIBILITY_INTERACTION_CLIENT = 0x0000000000040000L;
+
+ long FLAGS_LOGGING_NONE = 0x0000000000000000L;
+ long FLAGS_LOGGING_ALL = 0xFFFFFFFFFFFFFFFFL;
+
+ long FLAGS_ACCESSIBILITY_MANAGER_CLIENT_STATES = FLAGS_ACCESSIBILITY_INTERACTION_CLIENT
+ | FLAGS_ACCESSIBILITY_SERVICE
+ | FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION
+ | FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK;
+
+ Map<String, Long> sNamesToFlags = Map.ofEntries(
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_ACCESSIBILITY_SERVICE_CONNECTION, FLAGS_ACCESSIBILITY_SERVICE_CONNECTION),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_ACCESSIBILITY_SERVICE_CLIENT, FLAGS_ACCESSIBILITY_SERVICE_CLIENT),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_ACCESSIBILITY_MANAGER, FLAGS_ACCESSIBILITY_MANAGER),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_ACCESSIBILITY_MANAGER_CLIENT, FLAGS_ACCESSIBILITY_MANAGER_CLIENT),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_ACCESSIBILITY_INTERACTION_CONNECTION,
+ FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK,
+ FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK,
+ FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_WINDOW_MAGNIFICATION_CONNECTION, FLAGS_WINDOW_MAGNIFICATION_CONNECTION),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_WINDOW_MANAGER_INTERNAL, FLAGS_WINDOW_MANAGER_INTERNAL),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
+ FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_MAGNIFICATION_CALLBACK, FLAGS_MAGNIFICATION_CALLBACK),
+ new AbstractMap.SimpleEntry<String, Long>(NAME_INPUT_FILTER, FLAGS_INPUT_FILTER),
+ new AbstractMap.SimpleEntry<String, Long>(NAME_GESTURE, FLAGS_GESTURE),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_ACCESSIBILITY_SERVICE, FLAGS_ACCESSIBILITY_SERVICE),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_PACKAGE_BROADCAST_RECEIVER, FLAGS_PACKAGE_BROADCAST_RECEIVER),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_USER_BROADCAST_RECEIVER, FLAGS_USER_BROADCAST_RECEIVER),
+ new AbstractMap.SimpleEntry<String, Long>(NAME_FINGERPRINT, FLAGS_FINGERPRINT),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_ACCESSIBILITY_INTERACTION_CLIENT, FLAGS_ACCESSIBILITY_INTERACTION_CLIENT),
+ new AbstractMap.SimpleEntry<String, Long>(NAME_NONE, FLAGS_LOGGING_NONE),
+ new AbstractMap.SimpleEntry<String, Long>(NAME_ALL_LOGGINGS, FLAGS_LOGGING_ALL));
+
+ /**
+ * Get the flags of the logging types by the given names.
+ * The names list contains logging type names in lower case.
+ */
+ static long getLoggingFlagsFromNames(List<String> names) {
+ long types = FLAGS_LOGGING_NONE;
+ for (String name : names) {
+ long flag = sNamesToFlags.get(name);
+ types |= flag;
+ }
+ return types;
+ }
+
+ /**
+ * Get the list of the names of logging types by the given flags.
+ */
+ static List<String> getNamesOfLoggingTypes(long flags) {
+ List<String> list = new ArrayList<String>();
+
+ for (Map.Entry<String, Long> entry : sNamesToFlags.entrySet()) {
+ if ((entry.getValue() & flags) != FLAGS_LOGGING_NONE) {
+ list.add(entry.getKey());
+ }
+ }
+
+ return list;
+ }
+
+ /**
+ * Whether the trace is enabled for any logging type.
+ */
+ boolean isA11yTracingEnabled();
+
+ /**
+ * Whether the trace is enabled for any of the given logging type.
+ */
+ boolean isA11yTracingEnabledForTypes(long typeIdFlags);
+
+ /**
+ * Get trace state to be sent to AccessibilityManager.
+ */
+ int getTraceStateForAccessibilityManagerClientState();
+
+ /**
+ * Start tracing for the given logging types.
+ */
+ void startTrace(long flagss);
+
+ /**
+ * Stop tracing.
+ */
+ void stopTrace();
+
+ /**
+ * Log one trace entry.
+ * @param where A string to identify this log entry, which can be used to search through the
+ * tracing file.
+ * @param loggingFlags Flags to identify which logging types this entry belongs to. This
+ * can be used to filter the log entries when generating tracing file.
+ */
+ void logTrace(String where, long loggingFlags);
+
+ /**
+ * Log one trace entry.
+ * @param where A string to identify this log entry, which can be used to filter/search
+ * through the tracing file.
+ * @param loggingFlags Flags to identify which logging types this entry belongs to. This
+ * can be used to filter the log entries when generating tracing file.
+ * @param callingParams The parameters for the method to be logged.
+ */
+ void logTrace(String where, long loggingFlags, String callingParams);
+
+ /**
+ * Log one trace entry. Accessibility services using AccessibilityInteractionClient to
+ * make screen content related requests use this API to log entry when receive callback.
+ * @param timestamp The timestamp when a callback is received.
+ * @param where A string to identify this log entry, which can be used to filter/search
+ * through the tracing file.
+ * @param loggingFlags Flags to identify which logging types this entry belongs to. This
+ * can be used to filter the log entries when generating tracing file.
+ * @param callingParams The parameters for the callback.
+ * @param processId The process id of the calling component.
+ * @param threadId The threadId of the calling component.
+ * @param callingUid The calling uid of the callback.
+ * @param callStack The call stack of the callback.
+ * @param ignoreStackElements ignore these call stack element
+ */
+ void logTrace(long timestamp, String where, long loggingFlags, String callingParams,
+ int processId, long threadId, int callingUid, StackTraceElement[] callStack,
+ Set<String> ignoreStackElements);
+}
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 923b6f4..1e76bbf 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -118,6 +118,6 @@
void setFocusAppearance(int strokeWidth, int color);
- oneway void logTrace(long timestamp, String where, String callingParams, int processId,
- long threadId, int callingUid, in Bundle serializedCallingStackInBundle);
+ oneway void logTrace(long timestamp, String where, long loggingTypes, String callingParams,
+ int processId, long threadId, int callingUid, in Bundle serializedCallingStackInBundle);
}
diff --git a/core/java/android/accounts/Account.java b/core/java/android/accounts/Account.java
index 0d6a079..e6cdcc0 100644
--- a/core/java/android/accounts/Account.java
+++ b/core/java/android/accounts/Account.java
@@ -31,6 +31,7 @@
import com.android.internal.annotations.GuardedBy;
+import java.util.Objects;
import java.util.Set;
/**
@@ -86,6 +87,12 @@
if (TextUtils.isEmpty(type)) {
throw new IllegalArgumentException("the type must not be empty: " + type);
}
+ if (name.length() > 200) {
+ throw new IllegalArgumentException("account name is longer than 200 characters");
+ }
+ if (type.length() > 200) {
+ throw new IllegalArgumentException("account type is longer than 200 characters");
+ }
this.name = name;
this.type = type;
this.accessId = accessId;
diff --git a/core/java/android/accounts/OWNERS b/core/java/android/accounts/OWNERS
index 8dcc04a..6ad9d92 100644
--- a/core/java/android/accounts/OWNERS
+++ b/core/java/android/accounts/OWNERS
@@ -1,9 +1,4 @@
-carlosvaldivia@google.com
+jcivelli@google.com
dementyev@google.com
-sandrakwan@google.com
-hackbod@google.com
-svetoslavganov@google.com
-fkupolov@google.com
yamasani@google.com
omakoto@google.com
-
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 4a7fcd2..a836625 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -476,6 +476,19 @@
}
/**
+ * Detaches the navigation bar from the app it was attached to during a transition.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS)
+ public void detachNavigationBarFromApp(@NonNull IBinder transition) {
+ try {
+ getService().detachNavigationBarFromApp(transition);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Information you can retrieve about a root task in the system.
* @hide
*/
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index cd2c12c..2d172c5 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -317,6 +317,16 @@
}
@Override
+ public @NonNull IntentSender getLaunchIntentSenderForPackage(@NonNull String packageName) {
+ try {
+ return mPM.getLaunchIntentSenderForPackage(packageName, mContext.getPackageName(),
+ mContext.getAttributionTag(), getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
public int[] getPackageGids(String packageName) throws NameNotFoundException {
return getPackageGids(packageName, 0);
}
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 74d51a0..9f97fad 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -330,4 +330,10 @@
* When the Picture-in-picture state has changed.
*/
void onPictureInPictureStateChanged(in PictureInPictureUiState pipState);
+
+ /**
+ * Re-attach navbar to the display during a recents transition.
+ * TODO(188595497): Remove this once navbar attachment is in shell.
+ */
+ void detachNavigationBarFromApp(in IBinder transition);
}
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index b95412f..7b96511 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -239,6 +239,12 @@
*/
public boolean isVisible;
+ /**
+ * Whether this task is sleeping due to sleeping display.
+ * @hide
+ */
+ public boolean isSleeping;
+
TaskInfo() {
// Do nothing
}
@@ -342,7 +348,8 @@
&& getWindowingMode() == that.getWindowingMode()
&& Objects.equals(taskDescription, that.taskDescription)
&& isFocused == that.isFocused
- && isVisible == that.isVisible;
+ && isVisible == that.isVisible
+ && isSleeping == that.isSleeping;
}
/**
@@ -396,6 +403,7 @@
parentTaskId = source.readInt();
isFocused = source.readBoolean();
isVisible = source.readBoolean();
+ isSleeping = source.readBoolean();
topActivityToken = source.readStrongBinder();
topActivityInSizeCompat = source.readBoolean();
mTopActivityLocusId = source.readTypedObject(LocusId.CREATOR);
@@ -434,6 +442,7 @@
dest.writeInt(parentTaskId);
dest.writeBoolean(isFocused);
dest.writeBoolean(isVisible);
+ dest.writeBoolean(isSleeping);
dest.writeStrongBinder(topActivityToken);
dest.writeBoolean(topActivityInSizeCompat);
dest.writeTypedObject(mTopActivityLocusId, flags);
@@ -462,6 +471,7 @@
+ " parentTaskId=" + parentTaskId
+ " isFocused=" + isFocused
+ " isVisible=" + isVisible
+ + " isSleeping=" + isSleeping
+ " topActivityToken=" + topActivityToken
+ " topActivityInSizeCompat=" + topActivityInSizeCompat
+ " locusId= " + mTopActivityLocusId
diff --git a/core/java/android/app/usage/OWNERS b/core/java/android/app/usage/OWNERS
index a33d0ad..9668f80 100644
--- a/core/java/android/app/usage/OWNERS
+++ b/core/java/android/app/usage/OWNERS
@@ -3,3 +3,5 @@
yamasani@google.com
mwachens@google.com
varunshah@google.com
+
+per-file *StorageStats* = file:/core/java/android/os/storage/OWNERS
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index ded5e6e..b0b2478 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -3054,6 +3054,9 @@
return true;
}
return false;
+ } else if (profile == BluetoothProfile.VOLUME_CONTROL) {
+ BluetoothVolumeControl vcs = new BluetoothVolumeControl(context, listener, this);
+ return true;
} else {
return false;
}
@@ -3142,6 +3145,11 @@
case BluetoothProfile.HEARING_AID:
BluetoothHearingAid hearingAid = (BluetoothHearingAid) proxy;
hearingAid.close();
+ break;
+ case BluetoothProfile.VOLUME_CONTROL:
+ BluetoothVolumeControl vcs = (BluetoothVolumeControl) proxy;
+ vcs.close();
+ break;
}
}
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index 161c843..f9f65d0 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -213,12 +213,18 @@
int LE_AUDIO = 22;
/**
+ * Volume Control profile
+ *
+ */
+ int VOLUME_CONTROL = 23;
+
+ /**
* Max profile ID. This value should be updated whenever a new profile is added to match
* the largest value assigned to a profile.
*
* @hide
*/
- int MAX_PROFILE_ID = 22;
+ int MAX_PROFILE_ID = 23;
/**
* Default priority for devices that we try to auto-connect to and
diff --git a/core/java/android/bluetooth/BluetoothVolumeControl.java b/core/java/android/bluetooth/BluetoothVolumeControl.java
new file mode 100644
index 0000000..6d515a1
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothVolumeControl.java
@@ -0,0 +1,355 @@
+/*
+ * Copyright 2021 HIMSA II K/S - www.himsa.com.
+ * Represented by EHIMA - www.ehima.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.bluetooth;
+
+import android.Manifest;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Attributable;
+import android.content.AttributionSource;
+import android.content.Context;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.CloseGuard;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class provides the public APIs to control the Bluetooth Volume Control service.
+ *
+ * <p>BluetoothVolumeControl is a proxy object for controlling the Bluetooth VC
+ * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
+ * the BluetoothVolumeControl proxy object.
+ * @hide
+ */
+@SystemApi
+public final class BluetoothVolumeControl implements BluetoothProfile, AutoCloseable {
+ private static final String TAG = "BluetoothVolumeControl";
+ private static final boolean DBG = true;
+ private static final boolean VDBG = false;
+
+ private CloseGuard mCloseGuard;
+
+ /**
+ * Intent used to broadcast the change in connection state of the Volume Control
+ * profile.
+ *
+ * <p>This intent will have 3 extras:
+ * <ul>
+ * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
+ * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
+ * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
+ * </ul>
+ *
+ * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+ * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
+ * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @SuppressLint("ActionValue")
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_CONNECTION_STATE_CHANGED =
+ "android.bluetooth.volume-control.profile.action.CONNECTION_STATE_CHANGED";
+
+ private BluetoothAdapter mAdapter;
+ private final AttributionSource mAttributionSource;
+ private final BluetoothProfileConnector<IBluetoothVolumeControl> mProfileConnector =
+ new BluetoothProfileConnector(this, BluetoothProfile.VOLUME_CONTROL, TAG,
+ IBluetoothVolumeControl.class.getName()) {
+ @Override
+ public IBluetoothVolumeControl getServiceInterface(IBinder service) {
+ return IBluetoothVolumeControl.Stub.asInterface(Binder.allowBlocking(service));
+ }
+ };
+
+ /**
+ * Create a BluetoothVolumeControl proxy object for interacting with the local
+ * Bluetooth Volume Control service.
+ */
+ /*package*/ BluetoothVolumeControl(Context context, ServiceListener listener,
+ BluetoothAdapter adapter) {
+ mAdapter = adapter;
+ mAttributionSource = adapter.getAttributionSource();
+ mProfileConnector.connect(context, listener);
+ mCloseGuard = new CloseGuard();
+ mCloseGuard.open("close");
+ }
+
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ protected void finalize() {
+ if (mCloseGuard != null) {
+ mCloseGuard.warnIfOpen();
+ }
+ close();
+ }
+
+ /*package*/
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public void close() {
+ mProfileConnector.disconnect();
+ }
+
+ private IBluetoothVolumeControl getService() { return mProfileConnector.getService(); }
+
+ /**
+ * Get the list of connected devices. Currently at most one.
+ *
+ * @return list of connected devices
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public @NonNull List<BluetoothDevice> getConnectedDevices() {
+ if (DBG) log("getConnectedDevices()");
+ final IBluetoothVolumeControl service = getService();
+ if (service != null && isEnabled()) {
+ try {
+ return Attributable.setAttributionSource(
+ service.getConnectedDevices(mAttributionSource), mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ return new ArrayList<BluetoothDevice>();
+ }
+ }
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
+ return new ArrayList<BluetoothDevice>();
+ }
+
+ /**
+ * Get the list of devices matching specified states. Currently at most one.
+ *
+ * @return list of matching devices
+ *
+ * @hide
+ */
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
+ public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+ if (DBG) log("getDevicesMatchingStates()");
+ final IBluetoothVolumeControl service = getService();
+ if (service != null && isEnabled()) {
+ try {
+ return Attributable.setAttributionSource(
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ return new ArrayList<BluetoothDevice>();
+ }
+ }
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
+ return new ArrayList<BluetoothDevice>();
+ }
+
+ /**
+ * Get connection state of device
+ *
+ * @return device connection state
+ *
+ * @hide
+ */
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
+ public int getConnectionState(BluetoothDevice device) {
+ if (DBG) log("getConnectionState(" + device + ")");
+ final IBluetoothVolumeControl service = getService();
+ if (service != null && isEnabled() && isValidDevice(device)) {
+ try {
+ return service.getConnectionState(device, mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+ }
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+
+ /**
+ * Tells remote device to set an absolute volume.
+ *
+ * @param volume Absolute volume to be set on remote device.
+ * Minimum value is 0 and maximum value is 255
+ * @hide
+ */
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public void setVolume(@Nullable BluetoothDevice device,
+ @IntRange(from = 0, to = 255) int volume) {
+ if (DBG)
+ log("setVolume(" + volume + ")");
+ final IBluetoothVolumeControl service = getService();
+ try {
+ if (service != null && isEnabled()) {
+ service.setVolume(device, volume, mAttributionSource);
+ return;
+ }
+ if (service == null)
+ Log.w(TAG, "Proxy not attached to service");
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ }
+ }
+
+ /**
+ * Set priority of the profile
+ *
+ * <p> The device should already be paired.
+ * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF},
+ *
+ * @param device Paired bluetooth device
+ * @param priority
+ * @return true if priority is set, false on error
+ * @hide
+ */
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public boolean setPriority(BluetoothDevice device, int priority) {
+ if (DBG) log("setPriority(" + device + ", " + priority + ")");
+ return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
+ }
+
+ /**
+ * Set connection policy of the profile
+ *
+ * <p> The device should already be paired.
+ * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
+ * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
+ *
+ * @param device Paired bluetooth device
+ * @param connectionPolicy is the connection policy to set to for this profile
+ * @return true if connectionPolicy is set, false on error
+ * @hide
+ */
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
+ @ConnectionPolicy int connectionPolicy) {
+ if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
+ final IBluetoothVolumeControl service = getService();
+ if (service != null && isEnabled() && isValidDevice(device)) {
+ if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+ return false;
+ }
+ try {
+ return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ return false;
+ }
+ }
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
+ return false;
+ }
+
+ /**
+ * Get the priority of the profile.
+ *
+ * <p> The priority can be any of:
+ * {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
+ *
+ * @param device Bluetooth device
+ * @return priority of the device
+ * @hide
+ */
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public int getPriority(BluetoothDevice device) {
+ if (VDBG) log("getPriority(" + device + ")");
+ return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
+ }
+
+ /**
+ * Get the connection policy of the profile.
+ *
+ * <p> The connection policy can be any of:
+ * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
+ * {@link #CONNECTION_POLICY_UNKNOWN}
+ *
+ * @param device Bluetooth device
+ * @return connection policy of the device
+ * @hide
+ */
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
+ if (VDBG) log("getConnectionPolicy(" + device + ")");
+ final IBluetoothVolumeControl service = getService();
+ if (service != null && isEnabled() && isValidDevice(device)) {
+ try {
+ return service.getConnectionPolicy(device, mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ }
+ }
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
+ return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ }
+
+ private boolean isEnabled() {
+ return mAdapter.getState() == BluetoothAdapter.STATE_ON;
+ }
+
+ private static boolean isValidDevice(@Nullable BluetoothDevice device) {
+ return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
+ }
+
+ private static void log(String msg) {
+ Log.d(TAG, msg);
+ }
+}
diff --git a/core/java/android/companion/Association.java b/core/java/android/companion/Association.java
index 9007d9d..b060ce2 100644
--- a/core/java/android/companion/Association.java
+++ b/core/java/android/companion/Association.java
@@ -53,8 +53,6 @@
-
-
// Code below generated by codegen v1.0.22.
//
// DO NOT MODIFY!
@@ -242,7 +240,7 @@
};
@DataClass.Generated(
- time = 1612832377589L,
+ time = 1611795283642L,
codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/companion/Association.java",
inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull java.lang.String mDeviceMacAddress\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate final boolean mNotifyOnDeviceNearby\nprivate final long mTimeApprovedMs\npublic int getUserId()\nprivate java.lang.String timeApprovedMsToString()\nclass Association extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstructor=true)")
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 688483a..19550de 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1805,7 +1805,7 @@
* the package name of the current installed package to be uninstalled.
* You can optionally supply {@link #EXTRA_RETURN_RESULT}.
* <p>
- * Output: If {@link #EXTRA_RETURN_RESULT}, returns whether the install
+ * Output: If {@link #EXTRA_RETURN_RESULT}, returns whether the uninstall
* succeeded.
* <p>
* Requires {@link android.Manifest.permission#REQUEST_DELETE_PACKAGES}
@@ -5033,7 +5033,7 @@
@SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_CAR_DOCK = "android.intent.category.CAR_DOCK";
/**
- * An activity to run when device is inserted into a car dock.
+ * An activity to run when device is inserted into a desk dock.
* Used with {@link #ACTION_MAIN} to launch an activity. For more
* information, see {@link android.app.UiModeManager}.
*/
@@ -5253,6 +5253,30 @@
@SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_APP_FILES = "android.intent.category.APP_FILES";
+ /**
+ * Used with {@link #ACTION_MAIN} to launch the weather application.
+ * The activity should be able to give the user information about the weather
+ * <p>NOTE: This should not be used as the primary key of an Intent,
+ * since it will not result in the app launching with the correct
+ * action and category. Instead, use this with
+ * {@link #makeMainSelectorActivity(String, String)} to generate a main
+ * Intent with this category in the selector.</p>
+ */
+ @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+ public static final String CATEGORY_APP_WEATHER = "android.intent.category.APP_WEATHER";
+
+ /**
+ * Used with {@link #ACTION_MAIN} to launch the fitness application.
+ * The activity should be able to give the user fitness information and manage workouts
+ * <p>NOTE: This should not be used as the primary key of an Intent,
+ * since it will not result in the app launching with the correct
+ * action and category. Instead, use this with
+ * {@link #makeMainSelectorActivity(String, String)} to generate a main
+ * Intent with this category in the selector.</p>
+ */
+ @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+ public static final String CATEGORY_APP_FITNESS = "android.intent.category.APP_FITNESS";
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Standard extra data keys.
diff --git a/core/java/android/content/PermissionChecker.java b/core/java/android/content/PermissionChecker.java
index a167cb3..8d3452e 100644
--- a/core/java/android/content/PermissionChecker.java
+++ b/core/java/android/content/PermissionChecker.java
@@ -597,7 +597,7 @@
* which will evaluate the permission access based on the current fg/bg state of the
* app and leave a record that the data was accessed.
*
- * <p>This API assumes the the {@link Binder#getCallingUid()} is the same as
+ * <p>This API assumes the {@link Binder#getCallingUid()} is the same as
* {@link Process#myUid()}.
*
* @param context Context for accessing resources.
@@ -634,7 +634,7 @@
* listener you should use this method which will evaluate the permission access based
* on the current fg/bg state of the app and leave a record that the data was accessed.
*
- * <p>This API assumes the the {@link Binder#getCallingUid()} is the same as
+ * <p>This API assumes the {@link Binder#getCallingUid()} is the same as
* {@link Process#myUid()}.
*
* @param context Context for accessing resources.
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index c2ac80e..f4657c3 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -757,6 +757,9 @@
void requestChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IOnChecksumsReadyListener onChecksumsReadyListener, int userId);
+ IntentSender getLaunchIntentSenderForPackage(String packageName, String callingPackage,
+ String featureId, int userId);
+
//------------------------------------------------------------------------
//
// The following binder interfaces have been moved to IPermissionManager
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 33a34be..2d3e2ee 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4463,12 +4463,17 @@
* main activity in the category {@link Intent#CATEGORY_LAUNCHER}. Returns
* <code>null</code> if neither are found.
*
+ * <p>Consider using {@link #getLaunchIntentSenderForPackage(String)} if
+ * the caller is not allowed to query for the <code>packageName</code>.
+ *
* @param packageName The name of the package to inspect.
*
* @return A fully-qualified {@link Intent} that can be used to launch the
* main activity in the package. Returns <code>null</code> if the package
* does not contain such an activity, or if <em>packageName</em> is not
* recognized.
+ *
+ * @see #getLaunchIntentSenderForPackage(String)
*/
public abstract @Nullable Intent getLaunchIntentForPackage(@NonNull String packageName);
@@ -4503,6 +4508,28 @@
public abstract @Nullable Intent getCarLaunchIntentForPackage(@NonNull String packageName);
/**
+ * Returns an {@link IntentSender} that can be used to launch a front-door activity in a
+ * package. This is used, for example, to implement an "open" button when browsing through
+ * packages. The current implementation is the same with
+ * {@link #getLaunchIntentForPackage(String)}. Instead of returning the {@link Intent}, it
+ * returns the {@link IntentSender} which is not restricted by the package visibility.
+ *
+ * <p>The caller can invoke
+ * {@link IntentSender#sendIntent(Context, int, Intent, IntentSender.OnFinished, Handler)}
+ * to launch the activity. An {@link IntentSender.SendIntentException} is thrown if the
+ * package does not contain such an activity, or if <em>packageName</em> is not recognized.
+ *
+ * @param packageName The name of the package to inspect.
+ * @return Returns a {@link IntentSender} to launch the activity.
+ *
+ * @see #getLaunchIntentForPackage(String)
+ */
+ public @NonNull IntentSender getLaunchIntentSenderForPackage(@NonNull String packageName) {
+ throw new UnsupportedOperationException("getLaunchIntentSenderForPackage not implemented"
+ + "in subclass");
+ }
+
+ /**
* Return an array of all of the POSIX secondary group IDs that have been
* assigned to the given package.
* <p>
@@ -6791,7 +6818,7 @@
@NonNull
public Resources getResourcesForApplication(@NonNull ApplicationInfo app, @Nullable
Configuration configuration) throws NameNotFoundException {
- throw new UnsupportedOperationException();
+ return getResourcesForApplication(app);
}
/**
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 4ff2624..697b72a 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -884,7 +884,11 @@
if ((flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0) {
if (p.mSigningDetails != SigningDetails.UNKNOWN) {
// only return a valid SigningInfo if there is signing information to report
- pi.signingInfo = new SigningInfo(p.mSigningDetails);
+ pi.signingInfo = new SigningInfo(
+ new android.content.pm.SigningDetails(p.mSigningDetails.signatures,
+ p.mSigningDetails.signatureSchemeVersion,
+ p.mSigningDetails.publicKeys,
+ p.mSigningDetails.pastSigningCertificates));
} else {
pi.signingInfo = null;
}
@@ -1399,7 +1403,7 @@
// must use v2 signing scheme
minSignatureScheme = SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2;
}
- SigningDetails verified;
+ final android.content.pm.SigningDetails verified;
if (skipVerify) {
// systemDir APKs are already trusted, save time by not verifying; since the signature
// is not verified and some system apps can have their V2+ signatures stripped allow
@@ -1414,9 +1418,13 @@
// we encountered. Note that for splits, certificates may have
// already been populated during an earlier parse of a base APK.
if (pkg.mSigningDetails == SigningDetails.UNKNOWN) {
- pkg.mSigningDetails = verified;
+ pkg.mSigningDetails = new SigningDetails(verified.getSignatures(),
+ verified.getSignatureSchemeVersion(),
+ verified.getPublicKeys(),
+ verified.getPastSigningCertificates());
} else {
- if (!Signature.areExactMatch(pkg.mSigningDetails.signatures, verified.signatures)) {
+ if (!Signature.areExactMatch(pkg.mSigningDetails.signatures,
+ verified.getSignatures())) {
throw new PackageParserException(
INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
apkPath + " has mismatched certificates");
diff --git a/core/java/android/content/pm/PackagePartitions.java b/core/java/android/content/pm/PackagePartitions.java
index 52ee4de..d157768 100644
--- a/core/java/android/content/pm/PackagePartitions.java
+++ b/core/java/android/content/pm/PackagePartitions.java
@@ -119,6 +119,9 @@
@Nullable
private final DeferredCanonicalFile mOverlayFolder;
+ @NonNull
+ private final File mNonConicalFolder;
+
private SystemPartition(@NonNull File folder, @PartitionType int type,
boolean containsPrivApp, boolean containsOverlay) {
this.type = type;
@@ -128,6 +131,7 @@
: null;
this.mOverlayFolder = containsOverlay ? new DeferredCanonicalFile(folder, "overlay")
: null;
+ this.mNonConicalFolder = folder;
}
public SystemPartition(@NonNull SystemPartition original) {
@@ -136,6 +140,7 @@
this.mAppFolder = original.mAppFolder;
this.mPrivAppFolder = original.mPrivAppFolder;
this.mOverlayFolder = original.mOverlayFolder;
+ this.mNonConicalFolder = original.mNonConicalFolder;
}
/**
@@ -153,6 +158,12 @@
return mFolder.getFile();
}
+ /** Returns the non-canonical folder of the partition. */
+ @NonNull
+ public File getNonConicalFolder() {
+ return mNonConicalFolder;
+ }
+
/** Returns the canonical app folder of the partition. */
@Nullable
public File getAppFolder() {
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 691c69c..a5d97f9 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -211,6 +211,8 @@
* Additional flag for {@link #protectionLevel}, corresponding to the
* {@code documenter} value of {@link android.R.attr#protectionLevel}.
*
+ * @deprecated this protectionLevel is obsolete. Permissions previously granted
+ * through this protectionLevel have been migrated to use <code>role</code> instead
* @hide
*/
@SystemApi
@@ -309,7 +311,6 @@
PROTECTION_FLAG_OEM,
PROTECTION_FLAG_VENDOR_PRIVILEGED,
PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER,
- PROTECTION_FLAG_DOCUMENTER,
PROTECTION_FLAG_CONFIGURATOR,
PROTECTION_FLAG_INCIDENT_REPORT_APPROVER,
PROTECTION_FLAG_APP_PREDICTOR,
@@ -561,9 +562,6 @@
if ((level & PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER) != 0) {
protLevel.append("|textClassifier");
}
- if ((level & PermissionInfo.PROTECTION_FLAG_DOCUMENTER) != 0) {
- protLevel.append("|documenter");
- }
if ((level & PROTECTION_FLAG_CONFIGURATOR) != 0) {
protLevel.append("|configurator");
}
diff --git a/core/java/android/content/pm/Signature.java b/core/java/android/content/pm/Signature.java
index bce4b87..3f5c5d2 100644
--- a/core/java/android/content/pm/Signature.java
+++ b/core/java/android/content/pm/Signature.java
@@ -256,7 +256,7 @@
try {
if (obj != null) {
Signature other = (Signature)obj;
- // Note, some classes, such as PackageParser.SigningDetails, rely on equals
+ // Note, some classes, such as SigningDetails, rely on equals
// only comparing the mSignature arrays without the flags.
return this == other || Arrays.equals(mSignature, other.mSignature);
}
diff --git a/core/java/android/content/pm/SigningDetails.java b/core/java/android/content/pm/SigningDetails.java
new file mode 100644
index 0000000..584a058
--- /dev/null
+++ b/core/java/android/content/pm/SigningDetails.java
@@ -0,0 +1,928 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.PackageUtils;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DataClass;
+
+import libcore.util.HexEncoding;
+
+import java.security.PublicKey;
+import java.security.cert.CertificateException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A container for signing-related data of an application package.
+ *
+ * @hide
+ */
+@DataClass(genConstructor = false, genConstDefs = false, genParcelable = true, genAidl = false)
+public final class SigningDetails implements Parcelable {
+
+ private static final String TAG = "SigningDetails";
+
+ @IntDef({SignatureSchemeVersion.UNKNOWN,
+ SignatureSchemeVersion.JAR,
+ SignatureSchemeVersion.SIGNING_BLOCK_V2,
+ SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SignatureSchemeVersion.SIGNING_BLOCK_V4})
+ public @interface SignatureSchemeVersion {
+ int UNKNOWN = 0;
+ int JAR = 1;
+ int SIGNING_BLOCK_V2 = 2;
+ int SIGNING_BLOCK_V3 = 3;
+ int SIGNING_BLOCK_V4 = 4;
+ }
+
+ /** The signing certificates associated with this application package. */
+ private final @Nullable Signature[] mSignatures;
+
+ /** The signature scheme version for this application package. */
+ private final @SignatureSchemeVersion int mSignatureSchemeVersion;
+
+ /** The public keys set for the certificates. */
+ private final @Nullable ArraySet<PublicKey> mPublicKeys;
+
+ /**
+ * APK Signature Scheme v3 includes support for adding a proof-of-rotation record that
+ * contains two pieces of information:
+ * 1) the past signing certificates
+ * 2) the flags that APK wants to assign to each of the past signing certificates.
+ *
+ * This collection of {@code Signature} objects, each of which is formed from a former
+ * signing certificate of this APK before it was changed by signing certificate rotation,
+ * represents the first piece of information. It is the APK saying to the rest of the
+ * world: "hey if you trust the old cert, you can trust me!" This is useful, if for
+ * instance, the platform would like to determine whether or not to allow this APK to do
+ * something it would've allowed it to do under the old cert (like upgrade).
+ */
+ private final @Nullable Signature[] mPastSigningCertificates;
+
+ /** special value used to see if cert is in package - not exposed to callers */
+ private static final int PAST_CERT_EXISTS = 0;
+
+ @IntDef(flag = true,
+ value = {CertCapabilities.INSTALLED_DATA,
+ CertCapabilities.SHARED_USER_ID,
+ CertCapabilities.PERMISSION,
+ CertCapabilities.ROLLBACK})
+ public @interface CertCapabilities {
+
+ /** accept data from already installed pkg with this cert */
+ int INSTALLED_DATA = 1;
+
+ /** accept sharedUserId with pkg with this cert */
+ int SHARED_USER_ID = 2;
+
+ /** grant SIGNATURE permissions to pkgs with this cert */
+ int PERMISSION = 4;
+
+ /** allow pkg to update to one signed by this certificate */
+ int ROLLBACK = 8;
+
+ /** allow pkg to continue to have auth access gated by this cert */
+ int AUTH = 16;
+ }
+
+ /** A representation of unknown signing details. Use instead of null. */
+ public static final SigningDetails UNKNOWN = new SigningDetails(/* signatures */ null,
+ SignatureSchemeVersion.UNKNOWN, /* keys */ null, /* pastSigningCertificates */ null);
+
+ @VisibleForTesting
+ public SigningDetails(@Nullable Signature[] signatures,
+ @SignatureSchemeVersion int signatureSchemeVersion,
+ @Nullable ArraySet<PublicKey> keys, @Nullable Signature[] pastSigningCertificates) {
+ mSignatures = signatures;
+ mSignatureSchemeVersion = signatureSchemeVersion;
+ mPublicKeys = keys;
+ mPastSigningCertificates = pastSigningCertificates;
+ }
+
+ public SigningDetails(@Nullable Signature[] signatures,
+ @SignatureSchemeVersion int signatureSchemeVersion,
+ @Nullable Signature[] pastSigningCertificates)
+ throws CertificateException {
+ this(signatures, signatureSchemeVersion, toSigningKeys(signatures),
+ pastSigningCertificates);
+ }
+
+ public SigningDetails(@Nullable Signature[] signatures,
+ @SignatureSchemeVersion int signatureSchemeVersion)
+ throws CertificateException {
+ this(signatures, signatureSchemeVersion, /* pastSigningCertificates */ null);
+ }
+
+ public SigningDetails(@Nullable SigningDetails orig) {
+ if (orig != null) {
+ if (orig.mSignatures != null) {
+ mSignatures = orig.mSignatures.clone();
+ } else {
+ mSignatures = null;
+ }
+ mSignatureSchemeVersion = orig.mSignatureSchemeVersion;
+ mPublicKeys = new ArraySet<>(orig.mPublicKeys);
+ if (orig.mPastSigningCertificates != null) {
+ mPastSigningCertificates = orig.mPastSigningCertificates.clone();
+ } else {
+ mPastSigningCertificates = null;
+ }
+ } else {
+ mSignatures = null;
+ mSignatureSchemeVersion = SignatureSchemeVersion.UNKNOWN;
+ mPublicKeys = null;
+ mPastSigningCertificates = null;
+ }
+ }
+
+ /**
+ * Merges the signing lineage of this instance with the lineage in the provided {@code
+ * otherSigningDetails} when one has the same or an ancestor signer of the other.
+ *
+ * <p>Merging two signing lineages will result in a new {@code SigningDetails} instance
+ * containing the longest common lineage with the most restrictive capabilities. If the two
+ * lineages contain the same signers with the same capabilities then the instance on which
+ * this was invoked is returned without any changes. Similarly if neither instance has a
+ * lineage, or if neither has the same or an ancestor signer then this instance is returned.
+ *
+ * Following are some example results of this method for lineages with signers A, B, C, D:
+ * - lineage B merged with lineage A -> B returns lineage A -> B.
+ * - lineage A -> B merged with lineage B -> C returns lineage A -> B -> C
+ * - lineage A -> B with the {@code PERMISSION} capability revoked for A merged with
+ * lineage A -> B with the {@code SHARED_USER_ID} capability revoked for A returns
+ * lineage A -> B with both capabilities revoked for A.
+ * - lineage A -> B -> C merged with lineage A -> B -> D would return the original lineage
+ * A -> B -> C since the current signer of both instances is not the same or in the
+ * lineage of the other.
+ *
+ * @param otherSigningDetails The {@code SigningDetails} you would like to merge with.
+ * @return Merged {@code SigningDetails} instance when one has the same or an ancestor signer
+ * of the other. If neither instance has a lineage, or if neither has the same or an
+ * ancestor signer then this instance is returned.
+ */
+ public @NonNull SigningDetails mergeLineageWith(@NonNull SigningDetails otherSigningDetails) {
+ if (!hasPastSigningCertificates()) {
+ return otherSigningDetails.hasPastSigningCertificates()
+ && otherSigningDetails.hasAncestorOrSelf(this) ? otherSigningDetails : this;
+ }
+ if (!otherSigningDetails.hasPastSigningCertificates()) {
+ return this;
+ }
+ // Use the utility method to determine which SigningDetails instance is the descendant
+ // and to confirm that the signing lineage does not diverge.
+ SigningDetails descendantSigningDetails = getDescendantOrSelf(otherSigningDetails);
+ if (descendantSigningDetails == null) {
+ return this;
+ }
+ return descendantSigningDetails == this ? mergeLineageWithAncestorOrSelf(
+ otherSigningDetails) : otherSigningDetails.mergeLineageWithAncestorOrSelf(this);
+ }
+
+ /**
+ * Merges the signing lineage of this instance with the lineage of the ancestor (or same)
+ * signer in the provided {@code otherSigningDetails}.
+ *
+ * @param otherSigningDetails The {@code SigningDetails} you would like to merge with.
+ * @return Merged {@code SigningDetails} instance.
+ */
+ private @NonNull SigningDetails mergeLineageWithAncestorOrSelf(
+ @NonNull SigningDetails otherSigningDetails) {
+ // This method should only be called with instances that contain lineages.
+ int index = mPastSigningCertificates.length - 1;
+ int otherIndex = otherSigningDetails.mPastSigningCertificates.length - 1;
+ if (index < 0 || otherIndex < 0) {
+ return this;
+ }
+
+ List<Signature> mergedSignatures = new ArrayList<>();
+ boolean capabilitiesModified = false;
+ // If this is a descendant lineage then add all of the descendant signer(s) to the
+ // merged lineage until the ancestor signer is reached.
+ while (index >= 0 && !mPastSigningCertificates[index].equals(
+ otherSigningDetails.mPastSigningCertificates[otherIndex])) {
+ mergedSignatures.add(new Signature(mPastSigningCertificates[index--]));
+ }
+ // If the signing lineage was exhausted then the provided ancestor is not actually an
+ // ancestor of this lineage.
+ if (index < 0) {
+ return this;
+ }
+
+ do {
+ // Add the common signer to the merged lineage with the most restrictive
+ // capabilities of the two lineages.
+ Signature signature = mPastSigningCertificates[index--];
+ Signature ancestorSignature =
+ otherSigningDetails.mPastSigningCertificates[otherIndex--];
+ Signature mergedSignature = new Signature(signature);
+ int mergedCapabilities = signature.getFlags() & ancestorSignature.getFlags();
+ if (signature.getFlags() != mergedCapabilities) {
+ capabilitiesModified = true;
+ mergedSignature.setFlags(mergedCapabilities);
+ }
+ mergedSignatures.add(mergedSignature);
+ } while (index >= 0 && otherIndex >= 0 && mPastSigningCertificates[index].equals(
+ otherSigningDetails.mPastSigningCertificates[otherIndex]));
+
+ // If both lineages still have elements then their lineages have diverged; since this is
+ // not supported return the invoking instance.
+ if (index >= 0 && otherIndex >= 0) {
+ return this;
+ }
+
+ // Add any remaining elements from either lineage that is not yet exhausted to the
+ // the merged lineage.
+ while (otherIndex >= 0) {
+ mergedSignatures.add(new Signature(
+ otherSigningDetails.mPastSigningCertificates[otherIndex--]));
+ }
+ while (index >= 0) {
+ mergedSignatures.add(new Signature(mPastSigningCertificates[index--]));
+ }
+
+ // if this lineage already contains all the elements in the ancestor and none of the
+ // capabilities were changed then just return this instance.
+ if (mergedSignatures.size() == mPastSigningCertificates.length
+ && !capabilitiesModified) {
+ return this;
+ }
+ // Since the signatures were added to the merged lineage from newest to oldest reverse
+ // the list to ensure the oldest signer is at index 0.
+ Collections.reverse(mergedSignatures);
+ try {
+ return new SigningDetails(new Signature[]{new Signature(mSignatures[0])},
+ mSignatureSchemeVersion, mergedSignatures.toArray(new Signature[0]));
+ } catch (CertificateException e) {
+ Slog.e(TAG, "Caught an exception creating the merged lineage: ", e);
+ return this;
+ }
+ }
+
+ /**
+ * Returns whether this and the provided {@code otherSigningDetails} share a common
+ * ancestor.
+ *
+ * <p>The two SigningDetails have a common ancestor if any of the following conditions are
+ * met:
+ * - If neither has a lineage and their current signer(s) are equal.
+ * - If only one has a lineage and the signer of the other is the same or in the lineage.
+ * - If both have a lineage and their current signers are the same or one is in the lineage
+ * of the other, and their lineages do not diverge to different signers.
+ */
+ public boolean hasCommonAncestor(@NonNull SigningDetails otherSigningDetails) {
+ if (!hasPastSigningCertificates()) {
+ // If this instance does not have a lineage then it must either be in the ancestry
+ // of or the same signer of the otherSigningDetails.
+ return otherSigningDetails.hasAncestorOrSelf(this);
+ }
+ if (!otherSigningDetails.hasPastSigningCertificates()) {
+ return hasAncestorOrSelf(otherSigningDetails);
+ }
+ // If both have a lineage then use getDescendantOrSelf to obtain the descendant signing
+ // details; a null return from that method indicates there is no common lineage between
+ // the two or that they diverge at a point in the lineage.
+ return getDescendantOrSelf(otherSigningDetails) != null;
+ }
+
+ /**
+ * Returns whether this instance is currently signed, or has ever been signed, with a
+ * signing certificate from the provided {@link Set} of {@code certDigests}.
+ *
+ * <p>The provided {@code certDigests} should contain the SHA-256 digest of the DER encoding
+ * of each trusted certificate with the digest characters in upper case. If this instance
+ * has multiple signers then all signers must be in the provided {@code Set}. If this
+ * instance has a signing lineage then this method will return true if any of the previous
+ * signers in the lineage match one of the entries in the {@code Set}.
+ */
+ public boolean hasAncestorOrSelfWithDigest(@Nullable Set<String> certDigests) {
+ if (this == UNKNOWN || certDigests == null || certDigests.size() == 0) {
+ return false;
+ }
+ // If an app is signed by multiple signers then all of the signers must be in the Set.
+ if (mSignatures.length > 1) {
+ // If the Set has less elements than the number of signatures then immediately
+ // return false as there's no way to satisfy the requirement of all signatures being
+ // in the Set.
+ if (certDigests.size() < mSignatures.length) {
+ return false;
+ }
+ for (Signature signature : mSignatures) {
+ String signatureDigest = PackageUtils.computeSha256Digest(
+ signature.toByteArray());
+ if (!certDigests.contains(signatureDigest)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ String signatureDigest = PackageUtils.computeSha256Digest(mSignatures[0].toByteArray());
+ if (certDigests.contains(signatureDigest)) {
+ return true;
+ }
+ if (hasPastSigningCertificates()) {
+ // The last element in the pastSigningCertificates array is the current signer;
+ // since that was verified above just check all the signers in the lineage.
+ for (int i = 0; i < mPastSigningCertificates.length - 1; i++) {
+ signatureDigest = PackageUtils.computeSha256Digest(
+ mPastSigningCertificates[i].toByteArray());
+ if (certDigests.contains(signatureDigest)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns the SigningDetails with a descendant (or same) signer after verifying the
+ * descendant has the same, a superset, or a subset of the lineage of the ancestor.
+ *
+ * <p>If this instance and the provided {@code otherSigningDetails} do not share an
+ * ancestry, or if their lineages diverge then null is returned to indicate there is no
+ * valid descendant SigningDetails.
+ */
+ private @Nullable SigningDetails getDescendantOrSelf(
+ @NonNull SigningDetails otherSigningDetails) {
+ final SigningDetails descendantSigningDetails;
+ final SigningDetails ancestorSigningDetails;
+ if (hasAncestorOrSelf(otherSigningDetails)) {
+ // If the otherSigningDetails has the same signer or a signer in the lineage of this
+ // instance then treat this instance as the descendant.
+ descendantSigningDetails = this;
+ ancestorSigningDetails = otherSigningDetails;
+ } else if (otherSigningDetails.hasAncestor(this)) {
+ // The above check confirmed that the two instances do not have the same signer and
+ // the signer of otherSigningDetails is not in this instance's lineage; if this
+ // signer is in the otherSigningDetails lineage then treat this as the ancestor.
+ descendantSigningDetails = otherSigningDetails;
+ ancestorSigningDetails = this;
+ } else {
+ // The signers are not the same and neither has the current signer of the other in
+ // its lineage; return null to indicate there is no descendant signer.
+ return null;
+ }
+ // Once the descent (or same) signer is identified iterate through the ancestry until
+ // the current signer of the ancestor is found.
+ int descendantIndex = descendantSigningDetails.mPastSigningCertificates.length - 1;
+ int ancestorIndex = ancestorSigningDetails.mPastSigningCertificates.length - 1;
+ while (descendantIndex >= 0
+ && !descendantSigningDetails.mPastSigningCertificates[descendantIndex].equals(
+ ancestorSigningDetails.mPastSigningCertificates[ancestorIndex])) {
+ descendantIndex--;
+ }
+ // Since the ancestry was verified above the descendant lineage should never be
+ // exhausted, but if for some reason the ancestor signer is not found then return null.
+ if (descendantIndex < 0) {
+ return null;
+ }
+ // Once the common ancestor (or same) signer is found iterate over the lineage of both
+ // to ensure that they are either the same or one is a subset of the other.
+ do {
+ descendantIndex--;
+ ancestorIndex--;
+ } while (descendantIndex >= 0 && ancestorIndex >= 0
+ && descendantSigningDetails.mPastSigningCertificates[descendantIndex].equals(
+ ancestorSigningDetails.mPastSigningCertificates[ancestorIndex]));
+
+ // If both lineages still have elements then they diverge and cannot be considered a
+ // valid common lineage.
+ if (descendantIndex >= 0 && ancestorIndex >= 0) {
+ return null;
+ }
+ // Since one or both of the lineages was exhausted they are either the same or one is a
+ // subset of the other; return the valid descendant.
+ return descendantSigningDetails;
+ }
+
+ /** Returns true if the signing details have one or more signatures. */
+ public boolean hasSignatures() {
+ return mSignatures != null && mSignatures.length > 0;
+ }
+
+ /** Returns true if the signing details have past signing certificates. */
+ public boolean hasPastSigningCertificates() {
+ return mPastSigningCertificates != null && mPastSigningCertificates.length > 0;
+ }
+
+ /**
+ * Determines if the provided {@code oldDetails} is an ancestor of or the same as this one.
+ * If the {@code oldDetails} signing certificate appears in our pastSigningCertificates,
+ * then that means it has authorized a signing certificate rotation, which eventually leads
+ * to our certificate, and thus can be trusted. If this method evaluates to true, this
+ * SigningDetails object should be trusted if the previous one is.
+ */
+ public boolean hasAncestorOrSelf(@NonNull SigningDetails oldDetails) {
+ if (this == UNKNOWN || oldDetails == UNKNOWN) {
+ return false;
+ }
+ if (oldDetails.mSignatures.length > 1) {
+ // multiple-signer packages cannot rotate signing certs, so we just compare current
+ // signers for an exact match
+ return signaturesMatchExactly(oldDetails);
+ } else {
+ // we may have signing certificate rotation history, check to see if the oldDetails
+ // was one of our old signing certificates
+ return hasCertificate(oldDetails.mSignatures[0]);
+ }
+ }
+
+ /**
+ * Similar to {@code hasAncestorOrSelf}. Returns true only if this {@code SigningDetails}
+ * is a descendant of {@code oldDetails}, not if they're the same. This is used to
+ * determine if this object is newer than the provided one.
+ */
+ public boolean hasAncestor(@NonNull SigningDetails oldDetails) {
+ if (this == UNKNOWN || oldDetails == UNKNOWN) {
+ return false;
+ }
+ if (hasPastSigningCertificates() && oldDetails.mSignatures.length == 1) {
+ // the last entry in pastSigningCertificates is the current signer, ignore it
+ for (int i = 0; i < mPastSigningCertificates.length - 1; i++) {
+ if (mPastSigningCertificates[i].equals(oldDetails.mSignatures[0])) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns whether this {@code SigningDetails} has a signer in common with the provided
+ * {@code otherDetails} with the specified {@code flags} capabilities provided by this
+ * signer.
+ *
+ * <p>Note this method allows for the signing lineage to diverge, so this should only be
+ * used for instances where the only requirement is a common signer in the lineage with
+ * the specified capabilities. If the current signer of this instance is an ancestor of
+ * {@code otherDetails} then {@code true} is immediately returned since the current signer
+ * has all capabilities granted.
+ */
+ public boolean hasCommonSignerWithCapability(@NonNull SigningDetails otherDetails,
+ @CertCapabilities int flags) {
+ if (this == UNKNOWN || otherDetails == UNKNOWN) {
+ return false;
+ }
+ // If either is signed with more than one signer then both must be signed by the same
+ // signers to consider the capabilities granted.
+ if (mSignatures.length > 1 || otherDetails.mSignatures.length > 1) {
+ return signaturesMatchExactly(otherDetails);
+ }
+ // The Signature class does not use the granted capabilities in the hashCode
+ // computation, so a Set can be used to check for a common signer.
+ Set<Signature> otherSignatures = new ArraySet<>();
+ if (otherDetails.hasPastSigningCertificates()) {
+ otherSignatures.addAll(Arrays.asList(otherDetails.mPastSigningCertificates));
+ } else {
+ otherSignatures.addAll(Arrays.asList(otherDetails.mSignatures));
+ }
+ // If the current signer of this instance is an ancestor of the other than return true
+ // since all capabilities are granted to the current signer.
+ if (otherSignatures.contains(mSignatures[0])) {
+ return true;
+ }
+ if (hasPastSigningCertificates()) {
+ // Since the current signer was checked above and the last signature in the
+ // pastSigningCertificates is the current signer skip checking the last element.
+ for (int i = 0; i < mPastSigningCertificates.length - 1; i++) {
+ if (otherSignatures.contains(mPastSigningCertificates[i])) {
+ // If the caller specified multiple capabilities ensure all are set.
+ if ((mPastSigningCertificates[i].getFlags() & flags) == flags) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Determines if the provided {@code oldDetails} is an ancestor of this one, and whether or
+ * not this one grants it the provided capability, represented by the {@code flags}
+ * parameter. In the event of signing certificate rotation, a package may still interact
+ * with entities signed by its old signing certificate and not want to break previously
+ * functioning behavior. The {@code flags} value determines which capabilities the app
+ * signed by the newer signing certificate would like to continue to give to its previous
+ * signing certificate(s).
+ */
+ public boolean checkCapability(@NonNull SigningDetails oldDetails,
+ @CertCapabilities int flags) {
+ if (this == UNKNOWN || oldDetails == UNKNOWN) {
+ return false;
+ }
+ if (oldDetails.mSignatures.length > 1) {
+ // multiple-signer packages cannot rotate signing certs, so we must have an exact
+ // match, which also means all capabilities are granted
+ return signaturesMatchExactly(oldDetails);
+ } else {
+ // we may have signing certificate rotation history, check to see if the oldDetails
+ // was one of our old signing certificates, and if we grant it the capability it's
+ // requesting
+ return hasCertificate(oldDetails.mSignatures[0], flags);
+ }
+ }
+
+ /**
+ * A special case of {@code checkCapability} which re-encodes both sets of signing
+ * certificates to counteract a previous re-encoding.
+ */
+ public boolean checkCapabilityRecover(@NonNull SigningDetails oldDetails,
+ @CertCapabilities int flags) throws CertificateException {
+ if (oldDetails == UNKNOWN || this == UNKNOWN) {
+ return false;
+ }
+ if (hasPastSigningCertificates() && oldDetails.mSignatures.length == 1) {
+ // signing certificates may have rotated, check entire history for effective match
+ for (int i = 0; i < mPastSigningCertificates.length; i++) {
+ if (Signature.areEffectiveMatch(
+ oldDetails.mSignatures[0],
+ mPastSigningCertificates[i])
+ && mPastSigningCertificates[i].getFlags() == flags) {
+ return true;
+ }
+ }
+ } else {
+ return Signature.areEffectiveMatch(oldDetails.mSignatures, mSignatures);
+ }
+ return false;
+ }
+
+ /**
+ * Determine if {@code signature} is in this SigningDetails' signing certificate history,
+ * including the current signer. Automatically returns false if this object has multiple
+ * signing certificates, since rotation is only supported for single-signers; this is
+ * enforced by {@code hasCertificateInternal}.
+ */
+ public boolean hasCertificate(@NonNull Signature signature) {
+ return hasCertificateInternal(signature, PAST_CERT_EXISTS);
+ }
+
+ /**
+ * Determine if {@code signature} is in this SigningDetails' signing certificate history,
+ * including the current signer, and whether or not it has the given permission.
+ * Certificates which match our current signer automatically get all capabilities.
+ * Automatically returns false if this object has multiple signing certificates, since
+ * rotation is only supported for single-signers.
+ */
+ public boolean hasCertificate(@NonNull Signature signature, @CertCapabilities int flags) {
+ return hasCertificateInternal(signature, flags);
+ }
+
+ /** Convenient wrapper for calling {@code hasCertificate} with certificate's raw bytes. */
+ public boolean hasCertificate(byte[] certificate) {
+ Signature signature = new Signature(certificate);
+ return hasCertificate(signature);
+ }
+
+ private boolean hasCertificateInternal(@NonNull Signature signature, int flags) {
+ if (this == UNKNOWN) {
+ return false;
+ }
+
+ // only single-signed apps can have pastSigningCertificates
+ if (hasPastSigningCertificates()) {
+ // check all past certs, except for the current one, which automatically gets all
+ // capabilities, since it is the same as the current signature
+ for (int i = 0; i < mPastSigningCertificates.length - 1; i++) {
+ if (mPastSigningCertificates[i].equals(signature)) {
+ if (flags == PAST_CERT_EXISTS
+ || (flags & mPastSigningCertificates[i].getFlags()) == flags) {
+ return true;
+ }
+ }
+ }
+ }
+
+ // not in previous certs signing history, just check the current signer and make sure
+ // we are singly-signed
+ return mSignatures.length == 1 && mSignatures[0].equals(signature);
+ }
+
+ /**
+ * Determines if the provided {@code sha256String} is an ancestor of this one, and whether
+ * or not this one grants it the provided capability, represented by the {@code flags}
+ * parameter. In the event of signing certificate rotation, a package may still interact
+ * with entities signed by its old signing certificate and not want to break previously
+ * functioning behavior. The {@code flags} value determines which capabilities the app
+ * signed by the newer signing certificate would like to continue to give to its previous
+ * signing certificate(s).
+ *
+ * @param sha256String A hex-encoded representation of a sha256 digest. In the case of an
+ * app with multiple signers, this represents the hex-encoded sha256
+ * digest of the combined hex-encoded sha256 digests of each individual
+ * signing certificate according to {@link
+ * PackageUtils#computeSignaturesSha256Digest(Signature[])}
+ */
+ public boolean checkCapability(@Nullable String sha256String, @CertCapabilities int flags) {
+ if (this == UNKNOWN || TextUtils.isEmpty(sha256String)) {
+ return false;
+ }
+
+ // first see if the hash represents a single-signer in our signing history
+ final byte[] sha256Bytes = HexEncoding.decode(sha256String, false /* allowSingleChar */);
+ if (hasSha256Certificate(sha256Bytes, flags)) {
+ return true;
+ }
+
+ // Not in signing history, either represents multiple signatures or not a match.
+ // Multiple signers can't rotate, so no need to check flags, just see if the SHAs match.
+ // We already check the single-signer case above as part of hasSha256Certificate, so no
+ // need to verify we have multiple signers, just run the old check
+ // just consider current signing certs
+ final String[] mSignaturesSha256Digests =
+ PackageUtils.computeSignaturesSha256Digests(mSignatures);
+ final String mSignaturesSha256Digest =
+ PackageUtils.computeSignaturesSha256Digest(mSignaturesSha256Digests);
+ return mSignaturesSha256Digest.equals(sha256String);
+ }
+
+ /**
+ * Determine if the {@code sha256Certificate} is in this SigningDetails' signing certificate
+ * history, including the current signer. Automatically returns false if this object has
+ * multiple signing certificates, since rotation is only supported for single-signers.
+ */
+ public boolean hasSha256Certificate(byte[] sha256Certificate) {
+ return hasSha256CertificateInternal(sha256Certificate, PAST_CERT_EXISTS);
+ }
+
+ /**
+ * Determine if the {@code sha256Certificate} certificate hash corresponds to a signing
+ * certificate in this SigningDetails' signing certificate history, including the current
+ * signer, and whether or not it has the given permission. Certificates which match our
+ * current signer automatically get all capabilities. Automatically returns false if this
+ * object has multiple signing certificates, since rotation is only supported for
+ * single-signers.
+ */
+ public boolean hasSha256Certificate(byte[] sha256Certificate, @CertCapabilities int flags) {
+ return hasSha256CertificateInternal(sha256Certificate, flags);
+ }
+
+ private boolean hasSha256CertificateInternal(byte[] sha256Certificate, int flags) {
+ if (this == UNKNOWN) {
+ return false;
+ }
+ if (hasPastSigningCertificates()) {
+ // check all past certs, except for the last one, which automatically gets all
+ // capabilities, since it is the same as the current signature, and is checked below
+ for (int i = 0; i < mPastSigningCertificates.length - 1; i++) {
+ byte[] digest = PackageUtils.computeSha256DigestBytes(
+ mPastSigningCertificates[i].toByteArray());
+ if (Arrays.equals(sha256Certificate, digest)) {
+ if (flags == PAST_CERT_EXISTS
+ || (flags & mPastSigningCertificates[i].getFlags()) == flags) {
+ return true;
+ }
+ }
+ }
+ }
+
+ // not in previous certs signing history, just check the current signer
+ if (mSignatures.length == 1) {
+ byte[] digest = PackageUtils.computeSha256DigestBytes(mSignatures[0].toByteArray());
+ return Arrays.equals(sha256Certificate, digest);
+ }
+ return false;
+ }
+
+ /** Returns true if the signatures in this and other match exactly. */
+ public boolean signaturesMatchExactly(@NonNull SigningDetails other) {
+ return Signature.areExactMatch(mSignatures, other.mSignatures);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ boolean isUnknown = UNKNOWN == this;
+ dest.writeBoolean(isUnknown);
+ if (isUnknown) {
+ return;
+ }
+ dest.writeTypedArray(mSignatures, flags);
+ dest.writeInt(mSignatureSchemeVersion);
+ dest.writeArraySet(mPublicKeys);
+ dest.writeTypedArray(mPastSigningCertificates, flags);
+ }
+
+ protected SigningDetails(@NonNull Parcel in) {
+ final ClassLoader boot = Object.class.getClassLoader();
+ mSignatures = in.createTypedArray(Signature.CREATOR);
+ mSignatureSchemeVersion = in.readInt();
+ mPublicKeys = (ArraySet<PublicKey>) in.readArraySet(boot);
+ mPastSigningCertificates = in.createTypedArray(Signature.CREATOR);
+ }
+
+ public static final @NonNull Parcelable.Creator<SigningDetails> CREATOR =
+ new Creator<SigningDetails>() {
+ @Override
+ public SigningDetails createFromParcel(@NonNull Parcel source) {
+ if (source.readBoolean()) {
+ return UNKNOWN;
+ }
+ return new SigningDetails(source);
+ }
+
+ @Override
+ public SigningDetails[] newArray(int size) {
+ return new SigningDetails[size];
+ }
+ };
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (!(o instanceof SigningDetails)) return false;
+
+ final SigningDetails that = (SigningDetails) o;
+
+ if (mSignatureSchemeVersion != that.mSignatureSchemeVersion) return false;
+ if (!Signature.areExactMatch(mSignatures, that.mSignatures)) return false;
+ if (mPublicKeys != null) {
+ if (!mPublicKeys.equals((that.mPublicKeys))) {
+ return false;
+ }
+ } else if (that.mPublicKeys != null) {
+ return false;
+ }
+
+ // can't use Signature.areExactMatch() because order matters with the past signing certs
+ if (!Arrays.equals(mPastSigningCertificates, that.mPastSigningCertificates)) {
+ return false;
+ }
+ // The capabilities for the past signing certs must match as well.
+ for (int i = 0; i < mPastSigningCertificates.length; i++) {
+ if (mPastSigningCertificates[i].getFlags()
+ != that.mPastSigningCertificates[i].getFlags()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = +Arrays.hashCode(mSignatures);
+ result = 31 * result + mSignatureSchemeVersion;
+ result = 31 * result + (mPublicKeys != null ? mPublicKeys.hashCode() : 0);
+ result = 31 * result + Arrays.hashCode(mPastSigningCertificates);
+ return result;
+ }
+
+ /**
+ * Builder of {@code SigningDetails} instances.
+ */
+ public static class Builder {
+ private @NonNull Signature[] mSignatures;
+ private @SignatureSchemeVersion int mSignatureSchemeVersion =
+ SignatureSchemeVersion.UNKNOWN;
+ private @Nullable Signature[] mPastSigningCertificates;
+
+ public Builder() {
+ }
+
+ /** get signing certificates used to sign the current APK */
+ public SigningDetails.Builder setSignatures(@NonNull Signature[] signatures) {
+ mSignatures = signatures;
+ return this;
+ }
+
+ /** set the signature scheme version used to sign the APK */
+ public SigningDetails.Builder setSignatureSchemeVersion(
+ @SignatureSchemeVersion int signatureSchemeVersion) {
+ mSignatureSchemeVersion = signatureSchemeVersion;
+ return this;
+ }
+
+ /** set the signing certificates by which the APK proved it can be authenticated */
+ public SigningDetails.Builder setPastSigningCertificates(
+ @Nullable Signature[] pastSigningCertificates) {
+ mPastSigningCertificates = pastSigningCertificates;
+ return this;
+ }
+
+ private void checkInvariants() {
+ // must have signatures and scheme version set
+ if (mSignatures == null) {
+ throw new IllegalStateException("SigningDetails requires the current signing"
+ + " certificates.");
+ }
+ }
+ /** build a {@code SigningDetails} object */
+ public SigningDetails build()
+ throws CertificateException {
+ checkInvariants();
+ return new SigningDetails(mSignatures, mSignatureSchemeVersion,
+ mPastSigningCertificates);
+ }
+ }
+
+ /** Parses the public keys from the set of signatures. */
+ public static ArraySet<PublicKey> toSigningKeys(@NonNull Signature[] signatures)
+ throws CertificateException {
+ final ArraySet<PublicKey> keys = new ArraySet<>(signatures.length);
+ for (int i = 0; i < signatures.length; i++) {
+ keys.add(signatures[i].getPublicKey());
+ }
+ return keys;
+ }
+
+
+
+ // Code below generated by codegen v1.0.22.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/SigningDetails.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * The signing certificates associated with this application package.
+ */
+ @DataClass.Generated.Member
+ public @Nullable Signature[] getSignatures() {
+ return mSignatures;
+ }
+
+ /**
+ * The signature scheme version for this application package.
+ */
+ @DataClass.Generated.Member
+ public @SignatureSchemeVersion int getSignatureSchemeVersion() {
+ return mSignatureSchemeVersion;
+ }
+
+ /**
+ * The public keys set for the certificates.
+ */
+ @DataClass.Generated.Member
+ public @Nullable ArraySet<PublicKey> getPublicKeys() {
+ return mPublicKeys;
+ }
+
+ /**
+ * APK Signature Scheme v3 includes support for adding a proof-of-rotation record that
+ * contains two pieces of information:
+ * 1) the past signing certificates
+ * 2) the flags that APK wants to assign to each of the past signing certificates.
+ *
+ * This collection of {@code Signature} objects, each of which is formed from a former
+ * signing certificate of this APK before it was changed by signing certificate rotation,
+ * represents the first piece of information. It is the APK saying to the rest of the
+ * world: "hey if you trust the old cert, you can trust me!" This is useful, if for
+ * instance, the platform would like to determine whether or not to allow this APK to do
+ * something it would've allowed it to do under the old cert (like upgrade).
+ */
+ @DataClass.Generated.Member
+ public @Nullable Signature[] getPastSigningCertificates() {
+ return mPastSigningCertificates;
+ }
+
+ @DataClass.Generated(
+ time = 1616984092921L,
+ codegenVersion = "1.0.22",
+ sourceFile = "frameworks/base/core/java/android/content/pm/SigningDetails.java",
+ inputSignatures = "private static final java.lang.String TAG\nprivate final @android.annotation.Nullable android.content.pm.Signature[] mSignatures\nprivate final @android.content.pm.SigningDetails.SignatureSchemeVersion int mSignatureSchemeVersion\nprivate final @android.annotation.Nullable android.util.ArraySet<java.security.PublicKey> mPublicKeys\nprivate final @android.annotation.Nullable android.content.pm.Signature[] mPastSigningCertificates\nprivate static final int PAST_CERT_EXISTS\npublic static final android.content.pm.SigningDetails UNKNOWN\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.SigningDetails> CREATOR\npublic @android.annotation.NonNull android.content.pm.SigningDetails mergeLineageWith(android.content.pm.SigningDetails)\nprivate @android.annotation.NonNull android.content.pm.SigningDetails mergeLineageWithAncestorOrSelf(android.content.pm.SigningDetails)\npublic boolean hasCommonAncestor(android.content.pm.SigningDetails)\npublic boolean hasAncestorOrSelfWithDigest(java.util.Set<java.lang.String>)\nprivate @android.annotation.Nullable android.content.pm.SigningDetails getDescendantOrSelf(android.content.pm.SigningDetails)\npublic boolean hasSignatures()\npublic boolean hasPastSigningCertificates()\npublic boolean hasAncestorOrSelf(android.content.pm.SigningDetails)\npublic boolean hasAncestor(android.content.pm.SigningDetails)\npublic boolean hasCommonSignerWithCapability(android.content.pm.SigningDetails,int)\npublic boolean checkCapability(android.content.pm.SigningDetails,int)\npublic boolean checkCapabilityRecover(android.content.pm.SigningDetails,int)\npublic boolean hasCertificate(android.content.pm.Signature)\npublic boolean hasCertificate(android.content.pm.Signature,int)\npublic boolean hasCertificate(byte[])\nprivate boolean hasCertificateInternal(android.content.pm.Signature,int)\npublic boolean checkCapability(java.lang.String,int)\npublic boolean hasSha256Certificate(byte[])\npublic boolean hasSha256Certificate(byte[],int)\nprivate boolean hasSha256CertificateInternal(byte[],int)\npublic boolean signaturesMatchExactly(android.content.pm.SigningDetails)\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\npublic @java.lang.Override boolean equals(java.lang.Object)\npublic @java.lang.Override int hashCode()\npublic static android.util.ArraySet<java.security.PublicKey> toSigningKeys(android.content.pm.Signature[])\nclass SigningDetails extends java.lang.Object implements [android.os.Parcelable]\nprivate @android.annotation.NonNull android.content.pm.Signature[] mSignatures\nprivate @android.content.pm.SigningDetails.SignatureSchemeVersion int mSignatureSchemeVersion\nprivate @android.annotation.Nullable android.content.pm.Signature[] mPastSigningCertificates\npublic android.content.pm.SigningDetails.Builder setSignatures(android.content.pm.Signature[])\npublic android.content.pm.SigningDetails.Builder setSignatureSchemeVersion(int)\npublic android.content.pm.SigningDetails.Builder setPastSigningCertificates(android.content.pm.Signature[])\nprivate void checkInvariants()\npublic android.content.pm.SigningDetails build()\nclass Builder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false, genParcelable=true, genAidl=false)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/content/pm/SigningInfo.java b/core/java/android/content/pm/SigningInfo.java
index d14be9c..7459a90 100644
--- a/core/java/android/content/pm/SigningInfo.java
+++ b/core/java/android/content/pm/SigningInfo.java
@@ -16,7 +16,6 @@
package android.content.pm;
-
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
@@ -27,25 +26,25 @@
public final class SigningInfo implements Parcelable {
@NonNull
- private final PackageParser.SigningDetails mSigningDetails;
+ private final SigningDetails mSigningDetails;
public SigningInfo() {
- mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
+ mSigningDetails = SigningDetails.UNKNOWN;
}
/**
* @hide only packagemanager should be populating this
*/
- public SigningInfo(PackageParser.SigningDetails signingDetails) {
- mSigningDetails = new PackageParser.SigningDetails(signingDetails);
+ public SigningInfo(SigningDetails signingDetails) {
+ mSigningDetails = new SigningDetails(signingDetails);
}
public SigningInfo(SigningInfo orig) {
- mSigningDetails = new PackageParser.SigningDetails(orig.mSigningDetails);
+ mSigningDetails = new SigningDetails(orig.mSigningDetails);
}
private SigningInfo(Parcel source) {
- mSigningDetails = PackageParser.SigningDetails.CREATOR.createFromParcel(source);
+ mSigningDetails = SigningDetails.CREATOR.createFromParcel(source);
}
/**
@@ -53,7 +52,8 @@
* their identity is viewed as being the set of all signers, not just any one.
*/
public boolean hasMultipleSigners() {
- return mSigningDetails.signatures != null && mSigningDetails.signatures.length > 1;
+ return mSigningDetails.getSignatures() != null
+ && mSigningDetails.getSignatures().length > 1;
}
/**
@@ -65,8 +65,8 @@
* signing history, since it could change to a new signing certificate at any time.
*/
public boolean hasPastSigningCertificates() {
- return mSigningDetails.signatures != null
- && mSigningDetails.pastSigningCertificates != null;
+ return mSigningDetails.getPastSigningCertificates() != null
+ && mSigningDetails.getPastSigningCertificates().length > 0;
}
/**
@@ -93,11 +93,11 @@
} else if (!hasPastSigningCertificates()) {
// this package is only signed by one signer with no history, return it
- return mSigningDetails.signatures;
+ return mSigningDetails.getSignatures();
} else {
// this package has provided proof of past signing certificates, include them
- return mSigningDetails.pastSigningCertificates;
+ return mSigningDetails.getPastSigningCertificates();
}
}
@@ -111,7 +111,7 @@
* </note>
*/
public Signature[] getApkContentsSigners() {
- return mSigningDetails.signatures;
+ return mSigningDetails.getSignatures();
}
@Override
diff --git a/core/java/android/content/pm/parsing/ApkLite.java b/core/java/android/content/pm/parsing/ApkLite.java
index d8ec512..024c18c 100644
--- a/core/java/android/content/pm/parsing/ApkLite.java
+++ b/core/java/android/content/pm/parsing/ApkLite.java
@@ -19,7 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.PackageInfo;
-import android.content.pm.PackageParser.SigningDetails;
+import android.content.pm.SigningDetails;
import android.content.pm.VerifierInfo;
import com.android.internal.util.DataClass;
@@ -398,10 +398,10 @@
}
@DataClass.Generated(
- time = 1610596637723L,
+ time = 1616985847981L,
codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/content/pm/parsing/ApkLite.java",
- inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mRevisionCode\nprivate final int mInstallLocation\nprivate final int mMinSdkVersion\nprivate final int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.PackageParser.SigningDetails mSigningDetails\nprivate final boolean mFeatureSplit\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mProfileableByShell\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final boolean mOverlayIsStatic\nprivate final int mOverlayPriority\nprivate final int mRollbackDataPolicy\npublic long getLongVersionCode()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
+ inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mRevisionCode\nprivate final int mInstallLocation\nprivate final int mMinSdkVersion\nprivate final int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final boolean mFeatureSplit\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mProfileableByShell\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final boolean mOverlayIsStatic\nprivate final int mOverlayPriority\nprivate final int mRollbackDataPolicy\npublic long getLongVersionCode()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index 1e650a8..d6ac79d 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -28,6 +28,7 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
+import android.content.pm.SigningDetails;
import android.content.pm.VerifierInfo;
import android.content.pm.parsing.result.ParseInput;
import android.content.pm.parsing.result.ParseResult;
@@ -301,16 +302,15 @@
parser = apkAssets.openXml(ParsingPackageUtils.ANDROID_MANIFEST_FILENAME);
- final PackageParser.SigningDetails signingDetails;
+ final SigningDetails signingDetails;
if ((flags & ParsingPackageUtils.PARSE_COLLECT_CERTIFICATES) != 0) {
final boolean skipVerify = (flags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0;
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
try {
- ParseResult<PackageParser.SigningDetails> result =
- ParsingPackageUtils.getSigningDetails(input,
- apkFile.getAbsolutePath(), skipVerify, false,
- PackageParser.SigningDetails.UNKNOWN,
- DEFAULT_TARGET_SDK_VERSION);
+ final ParseResult<SigningDetails> result =
+ ParsingPackageUtils.getSigningDetails(input, apkFile.getAbsolutePath(),
+ skipVerify, /* isStaticSharedLibrary */ false,
+ SigningDetails.UNKNOWN, DEFAULT_TARGET_SDK_VERSION);
if (result.isError()) {
return input.error(result);
}
@@ -319,7 +319,7 @@
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
} else {
- signingDetails = PackageParser.SigningDetails.UNKNOWN;
+ signingDetails = SigningDetails.UNKNOWN;
}
return parseApkLite(input, apkPath, parser, signingDetails);
@@ -340,7 +340,7 @@
}
private static ParseResult<ApkLite> parseApkLite(ParseInput input, String codePath,
- XmlResourceParser parser, PackageParser.SigningDetails signingDetails)
+ XmlResourceParser parser, SigningDetails signingDetails)
throws IOException, XmlPullParserException {
ParseResult<Pair<String, String>> result = parsePackageSplitNames(input, parser);
if (result.isError()) {
diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
index c9054fd..4ccd67d 100644
--- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
+++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
@@ -32,7 +32,6 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
@@ -40,6 +39,7 @@
import android.content.pm.SELinuxUtil;
import android.content.pm.ServiceInfo;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.SigningInfo;
import android.content.pm.overlay.OverlayPaths;
import android.content.pm.parsing.component.ComponentParseUtils;
@@ -330,26 +330,26 @@
pi.isApex = true;
}
- PackageParser.SigningDetails signingDetails = pkg.getSigningDetails();
+ final SigningDetails signingDetails = pkg.getSigningDetails();
// deprecated method of getting signing certificates
if ((flags & PackageManager.GET_SIGNATURES) != 0) {
if (signingDetails.hasPastSigningCertificates()) {
// Package has included signing certificate rotation information. Return the oldest
// cert so that programmatic checks keep working even if unaware of key rotation.
pi.signatures = new Signature[1];
- pi.signatures[0] = signingDetails.pastSigningCertificates[0];
+ pi.signatures[0] = signingDetails.getPastSigningCertificates()[0];
} else if (signingDetails.hasSignatures()) {
// otherwise keep old behavior
- int numberOfSigs = signingDetails.signatures.length;
+ int numberOfSigs = signingDetails.getSignatures().length;
pi.signatures = new Signature[numberOfSigs];
- System.arraycopy(signingDetails.signatures, 0, pi.signatures, 0,
+ System.arraycopy(signingDetails.getSignatures(), 0, pi.signatures, 0,
numberOfSigs);
}
}
// replacement for GET_SIGNATURES
if ((flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0) {
- if (signingDetails != PackageParser.SigningDetails.UNKNOWN) {
+ if (signingDetails != SigningDetails.UNKNOWN) {
// only return a valid SigningInfo if there is signing information to report
pi.signingInfo = new SigningInfo(signingDetails);
} else {
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index ed68dbf..72cc929 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -25,7 +25,7 @@
import android.content.pm.FeatureGroupInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageManager.Property;
-import android.content.pm.PackageParser;
+import android.content.pm.SigningDetails;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedAttribution;
import android.content.pm.parsing.component.ParsedInstrumentation;
@@ -320,7 +320,7 @@
ParsingPackage setSharedUserLabel(int sharedUserLabel);
- ParsingPackage setSigningDetails(PackageParser.SigningDetails signingDetails);
+ ParsingPackage setSigningDetails(SigningDetails signingDetails);
ParsingPackage setSplitClassLoaderName(int splitIndex, String classLoaderName);
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index 4c44ba1..39e9a9b 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -32,7 +32,7 @@
import android.content.pm.FeatureInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.Property;
-import android.content.pm.PackageParser;
+import android.content.pm.SigningDetails;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedAttribution;
import android.content.pm.parsing.component.ParsedComponent;
@@ -292,7 +292,7 @@
@DataClass.ParcelWith(ForInternedString.class)
protected String volumeUuid;
@Nullable
- private PackageParser.SigningDetails signingDetails;
+ private SigningDetails signingDetails;
@NonNull
@DataClass.ParcelWith(ForInternedString.class)
@@ -723,6 +723,7 @@
@Override
public ParsingPackageImpl addImplicitPermission(String permission) {
+ addUsesPermission(new ParsedUsesPermission(permission, 0 /*usesPermissionFlags*/));
this.implicitPermissions = CollectionUtils.add(this.implicitPermissions,
TextUtils.safeIntern(permission));
return this;
@@ -1714,7 +1715,7 @@
@Nullable
@Override
- public PackageParser.SigningDetails getSigningDetails() {
+ public SigningDetails getSigningDetails() {
return signingDetails;
}
@@ -2276,7 +2277,7 @@
}
@Override
- public ParsingPackageImpl setSigningDetails(@Nullable PackageParser.SigningDetails value) {
+ public ParsingPackageImpl setSigningDetails(@Nullable SigningDetails value) {
signingDetails = value;
return this;
}
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index a6e189d..d5bd3a9 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -26,8 +26,8 @@
import android.content.pm.FeatureInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.Property;
-import android.content.pm.PackageParser;
import android.content.pm.ServiceInfo;
+import android.content.pm.SigningDetails;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedAttribution;
import android.content.pm.parsing.component.ParsedInstrumentation;
@@ -768,7 +768,7 @@
* The signature data of all APKs in this package, which must be exactly the same across the
* base and splits.
*/
- PackageParser.SigningDetails getSigningDetails();
+ SigningDetails getSigningDetails();
/**
* @see ApplicationInfo#splitClassLoaderNames
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 6fd5333..7c050b2 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -47,8 +47,8 @@
import android.content.pm.PackageManager.Property;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.PackageParser.SigningDetails;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.parsing.component.ComponentParseUtils;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedActivityUtils;
@@ -74,6 +74,7 @@
import android.content.pm.parsing.result.ParseInput.DeferredError;
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
+import android.content.pm.permission.CompatibilityPermissionInfo;
import android.content.pm.split.DefaultSplitAssetLoader;
import android.content.pm.split.SplitAssetDependencyLoader;
import android.content.pm.split.SplitAssetLoader;
@@ -904,7 +905,7 @@
);
}
- convertNewPermissions(pkg);
+ convertCompatPermissions(pkg);
convertSplitPermissions(pkg);
@@ -2790,31 +2791,16 @@
}
}
- private static void convertNewPermissions(ParsingPackage pkg) {
- final int NP = PackageParser.NEW_PERMISSIONS.length;
- StringBuilder newPermsMsg = null;
- for (int ip = 0; ip < NP; ip++) {
- final PackageParser.NewPermissionInfo npi
- = PackageParser.NEW_PERMISSIONS[ip];
- if (pkg.getTargetSdkVersion() >= npi.sdkVersion) {
+ private static void convertCompatPermissions(ParsingPackage pkg) {
+ for (int i = 0, size = CompatibilityPermissionInfo.COMPAT_PERMS.length; i < size; i++) {
+ final CompatibilityPermissionInfo info = CompatibilityPermissionInfo.COMPAT_PERMS[i];
+ if (pkg.getTargetSdkVersion() >= info.sdkVersion) {
break;
}
- if (!pkg.getRequestedPermissions().contains(npi.name)) {
- if (newPermsMsg == null) {
- newPermsMsg = new StringBuilder(128);
- newPermsMsg.append(pkg.getPackageName());
- newPermsMsg.append(": compat added ");
- } else {
- newPermsMsg.append(' ');
- }
- newPermsMsg.append(npi.name);
- pkg.addUsesPermission(new ParsedUsesPermission(npi.name, 0))
- .addImplicitPermission(npi.name);
+ if (!pkg.getRequestedPermissions().contains(info.name)) {
+ pkg.addImplicitPermission(info.name);
}
}
- if (newPermsMsg != null) {
- Slog.i(TAG, newPermsMsg.toString());
- }
}
private void convertSplitPermissions(ParsingPackage pkg) {
@@ -2830,8 +2816,7 @@
for (int in = 0; in < newPerms.size(); in++) {
final String perm = newPerms.get(in);
if (!requestedPermissions.contains(perm)) {
- pkg.addUsesPermission(new ParsedUsesPermission(perm, 0))
- .addImplicitPermission(perm);
+ pkg.addImplicitPermission(perm);
}
}
}
@@ -3056,7 +3041,8 @@
if (existingSigningDetails == SigningDetails.UNKNOWN) {
return input.success(verified);
} else {
- if (!Signature.areExactMatch(existingSigningDetails.signatures, verified.signatures)) {
+ if (!Signature.areExactMatch(existingSigningDetails.getSignatures(),
+ verified.getSignatures())) {
return input.error(INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
baseCodePath + " has mismatched certificates");
}
diff --git a/core/java/android/content/pm/parsing/result/ParseTypeImpl.java b/core/java/android/content/pm/parsing/result/ParseTypeImpl.java
index 7ac78b7..324612d 100644
--- a/core/java/android/content/pm/parsing/result/ParseTypeImpl.java
+++ b/core/java/android/content/pm/parsing/result/ParseTypeImpl.java
@@ -119,7 +119,6 @@
// how many APKs they're going through.
mDeferredErrors.erase();
}
- mPackageName = null;
mTargetSdkVersion = null;
return this;
}
diff --git a/core/java/android/content/pm/permission/CompatibilityPermissionInfo.java b/core/java/android/content/pm/permission/CompatibilityPermissionInfo.java
new file mode 100644
index 0000000..9198b95
--- /dev/null
+++ b/core/java/android/content/pm/permission/CompatibilityPermissionInfo.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.permission;
+
+import android.Manifest;
+import android.content.pm.parsing.component.ParsedUsesPermission;
+
+/**
+ * Implements compatibility support for permissions, and old applications
+ * will be automatically granted it.
+ *
+ * Compatibility permissions are permissions that are automatically granted to
+ * packages that target an SDK prior to when the permission was introduced.
+ * Sometimes the platform makes breaking behaviour changes and hides the legacy
+ * behaviour behind a permission. In these instances, we ensure applications
+ * targeting older platform versions are implicitly granted the correct set of
+ * permissions.
+ *
+ * @hide
+ */
+public class CompatibilityPermissionInfo extends ParsedUsesPermission {
+
+ public final int sdkVersion;
+
+ /**
+ * List of new permissions that have been added since 1.0.
+ *
+ * @hide
+ */
+ public static final CompatibilityPermissionInfo[] COMPAT_PERMS =
+ new CompatibilityPermissionInfo[]{
+ new CompatibilityPermissionInfo(Manifest.permission.WRITE_EXTERNAL_STORAGE,
+ android.os.Build.VERSION_CODES.DONUT, 0 /*usesPermissionFlags*/),
+ new CompatibilityPermissionInfo(Manifest.permission.READ_PHONE_STATE,
+ android.os.Build.VERSION_CODES.DONUT, 0 /*usesPermissionFlags*/)
+ };
+
+ private CompatibilityPermissionInfo(String name, int sdkVersion,
+ @UsesPermissionFlags int usesPermissionFlags) {
+ super(name, usesPermissionFlags);
+ this.sdkVersion = sdkVersion;
+ }
+}
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 0a76a9c..928e79c 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -497,6 +497,7 @@
if (type == Sensor.TYPE_PROXIMITY || type == Sensor.TYPE_SIGNIFICANT_MOTION
|| type == Sensor.TYPE_TILT_DETECTOR || type == Sensor.TYPE_WAKE_GESTURE
|| type == Sensor.TYPE_GLANCE_GESTURE || type == Sensor.TYPE_PICK_UP_GESTURE
+ || type == Sensor.TYPE_LOW_LATENCY_OFFBODY_DETECT
|| type == Sensor.TYPE_WRIST_TILT_GESTURE
|| type == Sensor.TYPE_DYNAMIC_SENSOR_META || type == Sensor.TYPE_HINGE_ANGLE) {
wakeUpSensor = true;
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index ecfc0d5..a3be415 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -346,7 +346,7 @@
@Retention(RetentionPolicy.SOURCE)
public @interface HdmiCecControl {}
- // -- Supported HDM-CEC versions.
+ // -- Supported HDMI-CEC versions.
/**
* Version constant for HDMI-CEC v1.4b.
*
@@ -371,23 +371,67 @@
@Retention(RetentionPolicy.SOURCE)
public @interface HdmiCecVersion {}
+ // -- Whether the Routing Control feature is enabled or disabled.
+ /**
+ * Routing Control feature enabled.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int ROUTING_CONTROL_ENABLED = 1;
+ /**
+ * Routing Control feature disabled.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int ROUTING_CONTROL_DISABLED = 0;
+ /**
+ * @hide
+ */
+ @IntDef(prefix = { "ROUTING_CONTROL_" }, value = {
+ ROUTING_CONTROL_ENABLED,
+ ROUTING_CONTROL_DISABLED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RoutingControl {}
+
// -- Scope of CEC power control messages sent by a playback device.
/**
- * Send CEC power control messages to TV only.
+ * Send CEC power control messages to TV only:
+ * Upon going to sleep, send {@code <Standby>} to TV only.
+ * Upon waking up, attempt to turn on the TV via {@code <One Touch Play>} but do not turn on the
+ * Audio system via {@code <System Audio Mode Request>}.
*
* @hide
*/
@SystemApi
public static final String POWER_CONTROL_MODE_TV = "to_tv";
/**
- * Broadcast CEC power control messages to all devices in the network.
+ * Send CEC power control messages to TV and Audio System:
+ * Upon going to sleep, send {@code <Standby>} to TV and Audio system.
+ * Upon waking up, attempt to turn on the TV via {@code <One Touch Play>} and attempt to turn on
+ * the Audio system via {@code <System Audio Mode Request>}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM = "to_tv_and_audio_system";
+ /**
+ * Broadcast CEC power control messages to all devices in the network:
+ * Upon going to sleep, send {@code <Standby>} to all devices in the network.
+ * Upon waking up, attempt to turn on the TV via {@code <One Touch Play>} and attempt to turn on
+ * the Audio system via {@code <System Audio Mode Request>}.
*
* @hide
*/
@SystemApi
public static final String POWER_CONTROL_MODE_BROADCAST = "broadcast";
/**
- * Don't send any CEC power control messages.
+ * Don't send any CEC power control messages:
+ * Upon going to sleep, do not send any {@code <Standby>} message.
+ * Upon waking up, do not turn on the TV via {@code <One Touch Play>} and do not turn on the
+ * Audio system via {@code <System Audio Mode Request>}.
*
* @hide
*/
@@ -398,6 +442,7 @@
*/
@StringDef(prefix = { "POWER_CONTROL_MODE_" }, value = {
POWER_CONTROL_MODE_TV,
+ POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM,
POWER_CONTROL_MODE_BROADCAST,
POWER_CONTROL_MODE_NONE
})
@@ -429,6 +474,31 @@
@Retention(RetentionPolicy.SOURCE)
public @interface ActiveSourceLostBehavior {}
+ // -- Whether System Audio Control is enabled or disabled.
+ /**
+ * System Audio Control enabled.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int SYSTEM_AUDIO_CONTROL_ENABLED = 1;
+ /**
+ * System Audio Control disabled.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int SYSTEM_AUDIO_CONTROL_DISABLED = 0;
+ /**
+ * @hide
+ */
+ @IntDef(prefix = { "SYSTEM_AUDIO_CONTROL_" }, value = {
+ SYSTEM_AUDIO_CONTROL_ENABLED,
+ SYSTEM_AUDIO_CONTROL_DISABLED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SystemAudioControl {}
+
// -- Whether System Audio Mode muting is enabled or disabled.
/**
* System Audio Mode muting enabled.
@@ -710,6 +780,13 @@
@SystemApi
public static final String CEC_SETTING_NAME_HDMI_CEC_VERSION = "hdmi_cec_version";
/**
+ * Name of a setting deciding whether the Routing Control feature is enabled.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String CEC_SETTING_NAME_ROUTING_CONTROL = "routing_control";
+ /**
* Name of a setting deciding on the power control mode.
*
* @hide
@@ -725,6 +802,14 @@
public static final String CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST =
"power_state_change_on_active_source_lost";
/**
+ * Name of a setting deciding whether System Audio Control is enabled.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL =
+ "system_audio_control";
+ /**
* Name of a setting deciding whether System Audio Muting is allowed.
*
* @hide
@@ -778,7 +863,7 @@
public static final String CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY =
"tv_wake_on_one_touch_play";
/**
- * Name of a setting deciding whether the device will also turn off other CEC devices
+ * Name of a setting deciding whether the TV will also turn off other CEC devices
* when it goes to standby mode.
*
* @hide
@@ -842,6 +927,7 @@
CEC_SETTING_NAME_HDMI_CEC_VERSION,
CEC_SETTING_NAME_POWER_CONTROL_MODE,
CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL,
CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
@@ -2021,6 +2107,56 @@
}
/**
+ * Set the status of Routing Control feature.
+ *
+ * <p>This allows to enable/disable Routing Control on the device.
+ * If enabled, the switch device will route to the correct input source on
+ * receiving Routing Control related messages. If disabled, you can only
+ * switch the input via controls on this device.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public void setRoutingControl(@NonNull @RoutingControl int value) {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ throw new RuntimeException("HdmiControlService is not available");
+ }
+ try {
+ mService.setCecSettingIntValue(CEC_SETTING_NAME_ROUTING_CONTROL, value);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get the current status of Routing Control feature.
+ *
+ * <p>Reflects whether Routing Control is currently enabled on the device.
+ * If enabled, the switch device will route to the correct input source on
+ * receiving Routing Control related messages. If disabled, you can only
+ * switch the input via controls on this device.
+ *
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ @RoutingControl
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public int getRoutingControl() {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ throw new RuntimeException("HdmiControlService is not available");
+ }
+ try {
+ return mService.getCecSettingIntValue(CEC_SETTING_NAME_ROUTING_CONTROL);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Set the status of Power Control.
*
* <p>Specifies to which devices Power Control messages should be sent:
@@ -2114,6 +2250,58 @@
}
/**
+ * Set the current status of System Audio Control.
+ *
+ * <p>Sets whether HDMI System Audio Control feature is enabled. If enabled,
+ * TV or Audio System will try to turn on the System Audio Mode if there's a
+ * connected CEC-enabled AV Receiver. Then an audio stream will be played on
+ * the AVR instead of TV speaker or Audio System speakers. If disabled, the
+ * System Audio Mode will never be activated.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public void setSystemAudioControl(@NonNull @SystemAudioControl int value) {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ throw new RuntimeException("HdmiControlService is not available");
+ }
+ try {
+ mService.setCecSettingIntValue(CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL, value);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get the current status of System Audio Control.
+ *
+ * <p>Reflects whether HDMI System Audio Control feature is enabled. If enabled,
+ * TV or Audio System will try to turn on the System Audio Mode if there's a
+ * connected CEC-enabled AV Receiver. Then an audio stream will be played on
+ * the AVR instead of TV speaker or Audio System speakers. If disabled, the
+ * System Audio Mode will never be activated.
+ *
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ @SystemAudioControl
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public int getSystemAudioControl() {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ throw new RuntimeException("HdmiControlService is not available");
+ }
+ try {
+ return mService.getCecSettingIntValue(CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Set the current status of System Audio Mode muting.
*
* <p>Sets whether the device should be muted when System Audio Mode is turned off.
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 17116e2..e5d8620 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -435,7 +435,7 @@
/**
* Enables an InputDevice.
* <p>
- * Requires {@link android.Manifest.permissions.DISABLE_INPUT_DEVICE}.
+ * Requires {@link android.Manifest.permission.DISABLE_INPUT_DEVICE}.
* </p>
*
* @param id The input device Id.
@@ -454,7 +454,7 @@
/**
* Disables an InputDevice.
* <p>
- * Requires {@link android.Manifest.permissions.DISABLE_INPUT_DEVICE}.
+ * Requires {@link android.Manifest.permission.DISABLE_INPUT_DEVICE}.
* </p>
*
* @param id The input device Id.
@@ -831,7 +831,7 @@
* Sets the TouchCalibration to apply to the specified input device's coordinates.
* <p>
* This method may have the side-effect of causing the input device in question
- * to be reconfigured. Requires {@link android.Manifest.permissions.SET_INPUT_CALIBRATION}.
+ * to be reconfigured. Requires {@link android.Manifest.permission.SET_INPUT_CALIBRATION}.
* </p>
*
* @param inputDeviceDescriptor The input device descriptor.
@@ -874,7 +874,7 @@
/**
* Sets the mouse pointer speed.
* <p>
- * Requires {@link android.Manifest.permissions.WRITE_SETTINGS}.
+ * Requires {@link android.Manifest.permission.WRITE_SETTINGS}.
* </p>
*
* @param context The application context.
@@ -1285,7 +1285,7 @@
* @param inputPort The port of the input device.
* @param displayPort The physical port of the associated display.
* <p>
- * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
+ * Requires {@link android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
* </p>
* @hide
*/
@@ -1302,7 +1302,7 @@
* static association for the cleared input port will be restored.
* @param inputPort The port of the input device to be cleared.
* <p>
- * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
+ * Requires {@link android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
* </p>
* @hide
*/
@@ -1320,7 +1320,7 @@
* @param inputDeviceName The name of the input device.
* @param displayUniqueId The unique id of the associated display.
* <p>
- * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
+ * Requires {@link android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
* </p>
* @hide
*/
@@ -1337,7 +1337,7 @@
* Removes a runtime association between the input device and display.
* @param inputDeviceName The name of the input device.
* <p>
- * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
+ * Requires {@link android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
* </p>
* @hide
*/
diff --git a/core/java/android/hardware/soundtrigger/ConversionUtil.java b/core/java/android/hardware/soundtrigger/ConversionUtil.java
index a5c9a7f..ad0dc09 100644
--- a/core/java/android/hardware/soundtrigger/ConversionUtil.java
+++ b/core/java/android/hardware/soundtrigger/ConversionUtil.java
@@ -19,19 +19,19 @@
import android.annotation.Nullable;
import android.media.AudioFormat;
import android.media.audio.common.AudioConfig;
-import android.media.soundtrigger_middleware.AudioCapabilities;
-import android.media.soundtrigger_middleware.ConfidenceLevel;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.Phrase;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.RecognitionMode;
-import android.media.soundtrigger_middleware.SoundModel;
+import android.media.soundtrigger.AudioCapabilities;
+import android.media.soundtrigger.ConfidenceLevel;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.Phrase;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseRecognitionExtra;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionMode;
+import android.media.soundtrigger.SoundModel;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
-import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
import android.os.ParcelFileDescriptor;
import android.os.SharedMemory;
@@ -43,7 +43,7 @@
class ConversionUtil {
public static SoundTrigger.ModuleProperties aidl2apiModuleDescriptor(
SoundTriggerModuleDescriptor aidlDesc) {
- SoundTriggerModuleProperties properties = aidlDesc.properties;
+ Properties properties = aidlDesc.properties;
return new SoundTrigger.ModuleProperties(
aidlDesc.handle,
properties.implementor,
@@ -194,19 +194,19 @@
}
public static SoundTrigger.RecognitionEvent aidl2apiRecognitionEvent(
- int modelHandle, RecognitionEvent aidlEvent) {
+ int modelHandle, int captureSession, RecognitionEvent aidlEvent) {
// The API recognition event doesn't allow for a null audio format, even though it doesn't
// always make sense. We thus replace it with a default.
AudioFormat audioFormat = aidl2apiAudioFormatWithDefault(aidlEvent.audioConfig);
return new SoundTrigger.GenericRecognitionEvent(
aidlEvent.status,
- modelHandle, aidlEvent.captureAvailable, aidlEvent.captureSession,
+ modelHandle, aidlEvent.captureAvailable, captureSession,
aidlEvent.captureDelayMs, aidlEvent.capturePreambleMs, aidlEvent.triggerInData,
audioFormat, aidlEvent.data);
}
public static SoundTrigger.RecognitionEvent aidl2apiPhraseRecognitionEvent(
- int modelHandle,
+ int modelHandle, int captureSession,
PhraseRecognitionEvent aidlEvent) {
SoundTrigger.KeyphraseRecognitionExtra[] apiExtras =
new SoundTrigger.KeyphraseRecognitionExtra[aidlEvent.phraseExtras.length];
@@ -218,7 +218,7 @@
AudioFormat audioFormat = aidl2apiAudioFormatWithDefault(aidlEvent.common.audioConfig);
return new SoundTrigger.KeyphraseRecognitionEvent(aidlEvent.common.status, modelHandle,
aidlEvent.common.captureAvailable,
- aidlEvent.common.captureSession, aidlEvent.common.captureDelayMs,
+ captureSession, aidlEvent.common.captureDelayMs,
aidlEvent.common.capturePreambleMs, aidlEvent.common.triggerInData,
audioFormat, aidlEvent.common.data,
apiExtras);
@@ -328,9 +328,9 @@
public static int api2aidlModelParameter(int apiParam) {
switch (apiParam) {
case ModelParams.THRESHOLD_FACTOR:
- return android.media.soundtrigger_middleware.ModelParameter.THRESHOLD_FACTOR;
+ return android.media.soundtrigger.ModelParameter.THRESHOLD_FACTOR;
default:
- return android.media.soundtrigger_middleware.ModelParameter.INVALID;
+ return android.media.soundtrigger.ModelParameter.INVALID;
}
}
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index 11f3e45..163e6f0 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -19,6 +19,7 @@
import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD;
import static android.Manifest.permission.RECORD_AUDIO;
import static android.Manifest.permission.SOUNDTRIGGER_DELEGATE_IDENTITY;
+import static android.system.OsConstants.EBUSY;
import static android.system.OsConstants.EINVAL;
import static android.system.OsConstants.ENODEV;
import static android.system.OsConstants.ENOSYS;
@@ -41,9 +42,9 @@
import android.media.permission.ClearCallingIdentityContext;
import android.media.permission.Identity;
import android.media.permission.SafeCloseable;
+import android.media.soundtrigger.Status;
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
-import android.media.soundtrigger_middleware.Status;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
@@ -91,6 +92,8 @@
public static final int STATUS_DEAD_OBJECT = -EPIPE;
/** @hide */
public static final int STATUS_INVALID_OPERATION = -ENOSYS;
+ /** @hide */
+ public static final int STATUS_BUSY = -EBUSY;
/*****************************************************************************
* A ModuleProperties describes a given sound trigger hardware module
@@ -1835,120 +1838,6 @@
}
}
- /**
- * Status codes for {@link SoundModelEvent}
- */
- /**
- * Sound Model was updated
- *
- * @hide
- */
- public static final int SOUNDMODEL_STATUS_UPDATED = 0;
-
- /**
- * A SoundModelEvent is provided by the
- * {@link StatusListener#onSoundModelUpdate(SoundModelEvent)}
- * callback when a sound model has been updated by the implementation
- *
- * @hide
- */
- public static class SoundModelEvent implements Parcelable {
- /** Status e.g {@link #SOUNDMODEL_STATUS_UPDATED} */
- public final int status;
- /** The updated sound model handle */
- public final int soundModelHandle;
- /** New sound model data */
- @NonNull
- public final byte[] data;
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- SoundModelEvent(int status, int soundModelHandle, @Nullable byte[] data) {
- this.status = status;
- this.soundModelHandle = soundModelHandle;
- this.data = data != null ? data : new byte[0];
- }
-
- public static final @android.annotation.NonNull Parcelable.Creator<SoundModelEvent> CREATOR
- = new Parcelable.Creator<SoundModelEvent>() {
- public SoundModelEvent createFromParcel(Parcel in) {
- return SoundModelEvent.fromParcel(in);
- }
-
- public SoundModelEvent[] newArray(int size) {
- return new SoundModelEvent[size];
- }
- };
-
- private static SoundModelEvent fromParcel(Parcel in) {
- int status = in.readInt();
- int soundModelHandle = in.readInt();
- byte[] data = in.readBlob();
- return new SoundModelEvent(status, soundModelHandle, data);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(status);
- dest.writeInt(soundModelHandle);
- dest.writeBlob(data);
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + Arrays.hashCode(data);
- result = prime * result + soundModelHandle;
- result = prime * result + status;
- return result;
- }
-
- @Override
- public boolean equals(@Nullable Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (getClass() != obj.getClass())
- return false;
- SoundModelEvent other = (SoundModelEvent) obj;
- if (!Arrays.equals(data, other.data))
- return false;
- if (soundModelHandle != other.soundModelHandle)
- return false;
- if (status != other.status)
- return false;
- return true;
- }
-
- @Override
- public String toString() {
- return "SoundModelEvent [status=" + status + ", soundModelHandle=" + soundModelHandle
- + ", data=" + (data == null ? 0 : data.length) + "]";
- }
- }
-
- /**
- * Native service state. {@link StatusListener#onServiceStateChange(int)}
- */
- // Keep in sync with system/core/include/system/sound_trigger.h
- /**
- * Sound trigger service is enabled
- *
- * @hide
- */
- public static final int SERVICE_STATE_ENABLED = 0;
- /**
- * Sound trigger service is disabled
- *
- * @hide
- */
- public static final int SERVICE_STATE_DISABLED = 1;
private static Object mServiceLock = new Object();
private static ISoundTriggerMiddlewareService mService;
@@ -1975,6 +1864,8 @@
return STATUS_DEAD_OBJECT;
case Status.INTERNAL_ERROR:
return STATUS_ERROR;
+ case Status.RESOURCE_CONTENTION:
+ return STATUS_BUSY;
}
return STATUS_ERROR;
}
@@ -2224,27 +2115,28 @@
*
* @hide
*/
- public static interface StatusListener {
+ public interface StatusListener {
/**
* Called when recognition succeeds of fails
*/
- public abstract void onRecognition(RecognitionEvent event);
+ void onRecognition(RecognitionEvent event);
/**
- * Called when a sound model has been updated
+ * Called when a sound model has been preemptively unloaded by the underlying
+ * implementation.
*/
- public abstract void onSoundModelUpdate(SoundModelEvent event);
+ void onModelUnloaded(int modelHandle);
/**
- * Called when the sound trigger native service state changes.
- * @param state Native service state. One of {@link SoundTrigger#SERVICE_STATE_ENABLED},
- * {@link SoundTrigger#SERVICE_STATE_DISABLED}
+ * Called whenever underlying conditions change, such that load/start operations that have
+ * previously failed or got preempted may now succeed. This is not a guarantee, merely a
+ * hint that the client may want to retry operations.
*/
- public abstract void onServiceStateChange(int state);
+ void onResourcesAvailable();
/**
* Called when the sound trigger native service dies
*/
- public abstract void onServiceDied();
+ void onServiceDied();
}
}
diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
index 431c99d..bf4b514 100644
--- a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
+++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
@@ -22,13 +22,13 @@
import android.media.permission.ClearCallingIdentityContext;
import android.media.permission.Identity;
import android.media.permission.SafeCloseable;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.SoundModel;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.SoundModel;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
@@ -48,7 +48,8 @@
private static final int EVENT_RECOGNITION = 1;
private static final int EVENT_SERVICE_DIED = 2;
- private static final int EVENT_SERVICE_STATE_CHANGE = 3;
+ private static final int EVENT_RESOURCES_AVAILABLE = 3;
+ private static final int EVENT_MODEL_UNLOADED = 4;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private int mId;
private EventHandlerDelegate mEventHandlerDelegate;
@@ -120,6 +121,7 @@
* @param soundModelHandle an array of int where the sound model handle will be returned.
* @return - {@link SoundTrigger#STATUS_OK} in case of success
* - {@link SoundTrigger#STATUS_ERROR} in case of unspecified error
+ * - {@link SoundTrigger#STATUS_BUSY} in case of transient resource constraints
* - {@link SoundTrigger#STATUS_PERMISSION_DENIED} if the caller does not have
* system permission
* - {@link SoundTrigger#STATUS_NO_INIT} if the native service cannot be reached
@@ -181,6 +183,7 @@
* recognition mode, keyphrases, users, minimum confidence levels...
* @return - {@link SoundTrigger#STATUS_OK} in case of success
* - {@link SoundTrigger#STATUS_ERROR} in case of unspecified error
+ * - {@link SoundTrigger#STATUS_BUSY} in case of transient resource constraints
* - {@link SoundTrigger#STATUS_PERMISSION_DENIED} if the caller does not have
* system permission
* - {@link SoundTrigger#STATUS_NO_INIT} if the native service cannot be reached
@@ -333,8 +336,11 @@
listener.onRecognition(
(SoundTrigger.RecognitionEvent) msg.obj);
break;
- case EVENT_SERVICE_STATE_CHANGE:
- listener.onServiceStateChange((int) msg.obj);
+ case EVENT_RESOURCES_AVAILABLE:
+ listener.onResourcesAvailable();
+ break;
+ case EVENT_MODEL_UNLOADED:
+ listener.onModelUnloaded((Integer) msg.obj);
break;
case EVENT_SERVICE_DIED:
listener.onServiceDied();
@@ -348,27 +354,32 @@
}
@Override
- public synchronized void onRecognition(int handle, RecognitionEvent event)
+ public synchronized void onRecognition(int handle, RecognitionEvent event,
+ int captureSession)
throws RemoteException {
Message m = mHandler.obtainMessage(EVENT_RECOGNITION,
- ConversionUtil.aidl2apiRecognitionEvent(handle, event));
+ ConversionUtil.aidl2apiRecognitionEvent(handle, captureSession, event));
mHandler.sendMessage(m);
}
@Override
- public synchronized void onPhraseRecognition(int handle, PhraseRecognitionEvent event)
+ public synchronized void onPhraseRecognition(int handle, PhraseRecognitionEvent event,
+ int captureSession)
throws RemoteException {
Message m = mHandler.obtainMessage(EVENT_RECOGNITION,
- ConversionUtil.aidl2apiPhraseRecognitionEvent(handle, event));
+ ConversionUtil.aidl2apiPhraseRecognitionEvent(handle, captureSession, event));
mHandler.sendMessage(m);
}
@Override
- public synchronized void onRecognitionAvailabilityChange(boolean available)
- throws RemoteException {
- Message m = mHandler.obtainMessage(EVENT_SERVICE_STATE_CHANGE,
- available ? SoundTrigger.SERVICE_STATE_ENABLED
- : SoundTrigger.SERVICE_STATE_DISABLED);
+ public void onModelUnloaded(int modelHandle) throws RemoteException {
+ Message m = mHandler.obtainMessage(EVENT_MODEL_UNLOADED, modelHandle);
+ mHandler.sendMessage(m);
+ }
+
+ @Override
+ public synchronized void onResourcesAvailable() throws RemoteException {
+ Message m = mHandler.obtainMessage(EVENT_RESOURCES_AVAILABLE);
mHandler.sendMessage(m);
}
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 964d7c1..c29a948 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -384,8 +384,9 @@
"android.hardware.usb.extra.ACCESSORY_START";
/**
- * A long extra indicating ms from boot to sent {@link #ACTION_USB_ACCESSORY_HANDSHAKE}
- * This is obtained with SystemClock.elapsedRealtime()
+
+ * A long extra indicating the timestamp just before
+ * sending {@link #ACTION_USB_ACCESSORY_HANDSHAKE}.
* Used in extras for {@link #ACTION_USB_ACCESSORY_HANDSHAKE} broadcasts.
*
* {@hide}
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index 3bde6fa..1d07a03 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -26,8 +26,10 @@
import android.telephony.Annotation.NetworkType;
import android.util.proto.ProtoOutputStream;
+import com.android.net.module.util.NetworkCapabilitiesUtils;
import com.android.net.module.util.NetworkIdentityUtils;
+import java.util.ArrayList;
import java.util.Objects;
/**
@@ -121,11 +123,37 @@
}
builder.append(", metered=").append(mMetered);
builder.append(", defaultNetwork=").append(mDefaultNetwork);
- // TODO(180557699): Print a human readable string for OEM managed state.
- builder.append(", oemManaged=").append(mOemManaged);
+ builder.append(", oemManaged=").append(getOemManagedNames(mOemManaged));
return builder.append("}").toString();
}
+ /**
+ * Get the human readable representation of a bitfield representing the OEM managed state of a
+ * network.
+ */
+ static String getOemManagedNames(int oemManaged) {
+ if (oemManaged == OEM_NONE) {
+ return "OEM_NONE";
+ }
+ final int[] bitPositions = NetworkCapabilitiesUtils.unpackBits(oemManaged);
+ final ArrayList<String> oemManagedNames = new ArrayList<String>();
+ for (int position : bitPositions) {
+ oemManagedNames.add(nameOfOemManaged(1 << position));
+ }
+ return String.join(",", oemManagedNames);
+ }
+
+ private static String nameOfOemManaged(int oemManagedBit) {
+ switch (oemManagedBit) {
+ case OEM_PAID:
+ return "OEM_PAID";
+ case OEM_PRIVATE:
+ return "OEM_PRIVATE";
+ default:
+ return "Invalid(" + oemManagedBit + ")";
+ }
+ }
+
public void dumpDebug(ProtoOutputStream proto, long tag) {
final long start = proto.start(tag);
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index 249154a..68917a8 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -427,7 +427,7 @@
builder.append(", subType=").append(mSubType);
}
if (mOemManaged != OEM_MANAGED_ALL) {
- builder.append(", oemManaged=").append(mOemManaged);
+ builder.append(", oemManaged=").append(getOemManagedNames(mOemManaged));
}
builder.append(", subscriberIdMatchRule=")
.append(subscriberIdMatchRuleToString(mSubscriberIdMatchRule));
@@ -781,6 +781,19 @@
}
}
+ private static String getOemManagedNames(int oemManaged) {
+ switch (oemManaged) {
+ case OEM_MANAGED_ALL:
+ return "OEM_MANAGED_ALL";
+ case OEM_MANAGED_NO:
+ return "OEM_MANAGED_NO";
+ case OEM_MANAGED_YES:
+ return "OEM_MANAGED_YES";
+ default:
+ return NetworkIdentity.getOemManagedNames(oemManaged);
+ }
+ }
+
/**
* Examine the given template and normalize if it refers to a "merged"
* mobile subscriber. We pick the "lowest" merged subscriber as the primary
diff --git a/core/java/android/net/vcn/OWNERS b/core/java/android/net/vcn/OWNERS
index 33b9f0f..2441e77 100644
--- a/core/java/android/net/vcn/OWNERS
+++ b/core/java/android/net/vcn/OWNERS
@@ -3,5 +3,5 @@
benedictwong@google.com
ckesting@google.com
evitayan@google.com
+junyin@google.com
nharold@google.com
-jchalard@google.com
\ No newline at end of file
diff --git a/core/java/android/os/AggregateBatteryConsumer.java b/core/java/android/os/AggregateBatteryConsumer.java
index ee86265..802387c 100644
--- a/core/java/android/os/AggregateBatteryConsumer.java
+++ b/core/java/android/os/AggregateBatteryConsumer.java
@@ -17,7 +17,13 @@
package android.os;
import android.annotation.NonNull;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
import java.io.PrintWriter;
/**
@@ -72,6 +78,43 @@
return mConsumedPowerMah;
}
+ /** Serializes this object to XML */
+ void writeToXml(TypedXmlSerializer serializer,
+ @BatteryUsageStats.AggregateBatteryConsumerScope int scope) throws IOException {
+ serializer.startTag(null, BatteryUsageStats.XML_TAG_AGGREGATE);
+ serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_SCOPE, scope);
+ serializer.attributeDouble(null, BatteryUsageStats.XML_ATTR_POWER, mConsumedPowerMah);
+ mPowerComponents.writeToXml(serializer);
+ serializer.endTag(null, BatteryUsageStats.XML_TAG_AGGREGATE);
+ }
+
+ /** Parses an XML representation and populates the BatteryUsageStats builder */
+ static void parseXml(TypedXmlPullParser parser, BatteryUsageStats.Builder builder)
+ throws XmlPullParserException, IOException {
+ final int scope = parser.getAttributeInt(null, BatteryUsageStats.XML_ATTR_SCOPE);
+ final Builder consumerBuilder = builder.getAggregateBatteryConsumerBuilder(scope);
+
+ int eventType = parser.getEventType();
+ if (eventType != XmlPullParser.START_TAG || !parser.getName().equals(
+ BatteryUsageStats.XML_TAG_AGGREGATE)) {
+ throw new XmlPullParserException("Invalid XML parser state");
+ }
+
+ consumerBuilder.setConsumedPower(
+ parser.getAttributeDouble(null, BatteryUsageStats.XML_ATTR_POWER));
+
+ while (!(eventType == XmlPullParser.END_TAG && parser.getName().equals(
+ BatteryUsageStats.XML_TAG_AGGREGATE))
+ && eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG) {
+ if (parser.getName().equals(BatteryUsageStats.XML_TAG_POWER_COMPONENTS)) {
+ PowerComponents.parseXml(parser, consumerBuilder.mPowerComponentsBuilder);
+ }
+ }
+ eventType = parser.next();
+ }
+ }
+
/**
* Builder for DeviceBatteryConsumer.
*/
@@ -91,6 +134,14 @@
}
/**
+ * Adds power and usage duration from the supplied AggregateBatteryConsumer.
+ */
+ public void add(AggregateBatteryConsumer aggregateBatteryConsumer) {
+ mConsumedPowerMah += aggregateBatteryConsumer.mConsumedPowerMah;
+ mPowerComponentsBuilder.addPowerAndDuration(aggregateBatteryConsumer.mPowerComponents);
+ }
+
+ /**
* Creates a read-only object out of the Builder values.
*/
@NonNull
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index 6c9f0f67..38529ec 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -20,16 +20,23 @@
import android.annotation.NonNull;
import android.util.Range;
import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.proto.ProtoOutputStream;
import com.android.internal.os.BatteryStatsHistory;
import com.android.internal.os.BatteryStatsHistoryIterator;
import com.android.internal.os.PowerCalculator;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
@@ -75,6 +82,34 @@
public static final int AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT = 2;
+ // XML tags and attributes for BatteryUsageStats persistence
+ static final String XML_TAG_BATTERY_USAGE_STATS = "battery_usage_stats";
+ static final String XML_TAG_AGGREGATE = "aggregate";
+ static final String XML_TAG_UID = "uid";
+ static final String XML_TAG_USER = "user";
+ static final String XML_TAG_POWER_COMPONENTS = "power_components";
+ static final String XML_TAG_COMPONENT = "component";
+ static final String XML_TAG_CUSTOM_COMPONENT = "custom_component";
+ static final String XML_ATTR_ID = "id";
+ static final String XML_ATTR_UID = "uid";
+ static final String XML_ATTR_USER_ID = "user_id";
+ static final String XML_ATTR_SCOPE = "scope";
+ static final String XML_ATTR_PREFIX_CUSTOM_COMPONENT = "custom_component_";
+ static final String XML_ATTR_START_TIMESTAMP = "start_timestamp";
+ static final String XML_ATTR_END_TIMESTAMP = "end_timestamp";
+ static final String XML_ATTR_POWER = "power";
+ static final String XML_ATTR_DURATION = "duration";
+ static final String XML_ATTR_MODEL = "model";
+ static final String XML_ATTR_BATTERY_CAPACITY = "battery_capacity";
+ static final String XML_ATTR_DISCHARGE_PERCENT = "discharge_pct";
+ static final String XML_ATTR_DISCHARGE_LOWER = "discharge_lower";
+ static final String XML_ATTR_DISCHARGE_UPPER = "discharge_upper";
+ static final String XML_ATTR_BATTERY_REMAINING = "battery_remaining";
+ static final String XML_ATTR_CHARGE_REMAINING = "charge_remaining";
+ static final String XML_ATTR_HIGHEST_DRAIN_PACKAGE = "highest_drain_package";
+ static final String XML_ATTR_TIME_IN_FOREGROUND = "time_in_foreground";
+ static final String XML_ATTR_TIME_IN_BACKGROUND = "time_in_background";
+
private static final int STATSD_PULL_ATOM_MAX_BYTES = 45000;
private final int mDischargePercentage;
@@ -96,11 +131,7 @@
private BatteryUsageStats(@NonNull Builder builder) {
mStatsStartTimestampMs = builder.mStatsStartTimestampMs;
mStatsEndTimestampMs = builder.mStatsEndTimestampMs;
- if (builder.mStatsDurationMs != -1) {
- mStatsDurationMs = builder.mStatsDurationMs;
- } else {
- mStatsDurationMs = mStatsEndTimestampMs - mStatsStartTimestampMs;
- }
+ mStatsDurationMs = builder.getStatsDuration();
mBatteryCapacityMah = builder.mBatteryCapacityMah;
mDischargePercentage = builder.mDischargePercentage;
mDischargedPowerLowerBound = builder.mDischargedPowerLowerBoundMah;
@@ -579,6 +610,109 @@
}
}
+ /** Serializes this object to XML */
+ public void writeXml(TypedXmlSerializer serializer) throws IOException {
+ serializer.startTag(null, XML_TAG_BATTERY_USAGE_STATS);
+
+ for (int i = 0; i < mCustomPowerComponentNames.length; i++) {
+ serializer.attribute(null, XML_ATTR_PREFIX_CUSTOM_COMPONENT + i,
+ mCustomPowerComponentNames[i]);
+ }
+
+ serializer.attributeLong(null, XML_ATTR_START_TIMESTAMP, mStatsStartTimestampMs);
+ serializer.attributeLong(null, XML_ATTR_END_TIMESTAMP, mStatsEndTimestampMs);
+ serializer.attributeLong(null, XML_ATTR_DURATION, mStatsDurationMs);
+ serializer.attributeDouble(null, XML_ATTR_BATTERY_CAPACITY, mBatteryCapacityMah);
+ serializer.attributeInt(null, XML_ATTR_DISCHARGE_PERCENT, mDischargePercentage);
+ serializer.attributeDouble(null, XML_ATTR_DISCHARGE_LOWER, mDischargedPowerLowerBound);
+ serializer.attributeDouble(null, XML_ATTR_DISCHARGE_UPPER, mDischargedPowerUpperBound);
+ serializer.attributeLong(null, XML_ATTR_BATTERY_REMAINING, mBatteryTimeRemainingMs);
+ serializer.attributeLong(null, XML_ATTR_CHARGE_REMAINING, mChargeTimeRemainingMs);
+
+ for (int scope = 0; scope < BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT;
+ scope++) {
+ mAggregateBatteryConsumers[scope].writeToXml(serializer, scope);
+ }
+ for (UidBatteryConsumer consumer : mUidBatteryConsumers) {
+ consumer.writeToXml(serializer);
+ }
+ for (UserBatteryConsumer consumer : mUserBatteryConsumers) {
+ consumer.writeToXml(serializer);
+ }
+ serializer.endTag(null, XML_TAG_BATTERY_USAGE_STATS);
+ }
+
+ /** Parses an XML representation of BatteryUsageStats */
+ public static BatteryUsageStats createFromXml(TypedXmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ Builder builder = null;
+ int eventType = parser.getEventType();
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG
+ && parser.getName().equals(XML_TAG_BATTERY_USAGE_STATS)) {
+ List<String> customComponentNames = new ArrayList<>();
+ int i = 0;
+ while (true) {
+ int index = parser.getAttributeIndex(null,
+ XML_ATTR_PREFIX_CUSTOM_COMPONENT + i);
+ if (index == -1) {
+ break;
+ }
+ customComponentNames.add(parser.getAttributeValue(index));
+ i++;
+ }
+
+ builder = new Builder(
+ customComponentNames.toArray(new String[0]), true);
+
+ builder.setStatsStartTimestamp(
+ parser.getAttributeLong(null, XML_ATTR_START_TIMESTAMP));
+ builder.setStatsEndTimestamp(
+ parser.getAttributeLong(null, XML_ATTR_END_TIMESTAMP));
+ builder.setStatsDuration(
+ parser.getAttributeLong(null, XML_ATTR_DURATION));
+ builder.setBatteryCapacity(
+ parser.getAttributeDouble(null, XML_ATTR_BATTERY_CAPACITY));
+ builder.setDischargePercentage(
+ parser.getAttributeInt(null, XML_ATTR_DISCHARGE_PERCENT));
+ builder.setDischargedPowerRange(
+ parser.getAttributeDouble(null, XML_ATTR_DISCHARGE_LOWER),
+ parser.getAttributeDouble(null, XML_ATTR_DISCHARGE_UPPER));
+ builder.setBatteryTimeRemainingMs(
+ parser.getAttributeLong(null, XML_ATTR_BATTERY_REMAINING));
+ builder.setChargeTimeRemainingMs(
+ parser.getAttributeLong(null, XML_ATTR_CHARGE_REMAINING));
+
+ eventType = parser.next();
+ break;
+ }
+ eventType = parser.next();
+ }
+
+ if (builder == null) {
+ throw new XmlPullParserException("No root element");
+ }
+
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG) {
+ switch (parser.getName()) {
+ case XML_TAG_AGGREGATE:
+ AggregateBatteryConsumer.parseXml(parser, builder);
+ break;
+ case XML_TAG_UID:
+ UidBatteryConsumer.createFromXml(parser, builder);
+ break;
+ case XML_TAG_USER:
+ UserBatteryConsumer.createFromXml(parser, builder);
+ break;
+ }
+ }
+ eventType = parser.next();
+ }
+
+ return builder.build();
+ }
+
/**
* Builder for BatteryUsageStats.
*/
@@ -658,6 +792,14 @@
return this;
}
+ private long getStatsDuration() {
+ if (mStatsDurationMs != -1) {
+ return mStatsDurationMs;
+ } else {
+ return mStatsEndTimestampMs - mStatsStartTimestampMs;
+ }
+ }
+
/**
* Sets the battery discharge amount since BatteryStats reset as percentage of the full
* charge.
@@ -738,6 +880,22 @@
}
/**
+ * Creates or returns a UidBatteryConsumer, which represents battery attribution
+ * data for an individual UID. This version of the method is not suitable for use
+ * with PowerCalculators.
+ */
+ @NonNull
+ public UidBatteryConsumer.Builder getOrCreateUidBatteryConsumerBuilder(int uid) {
+ UidBatteryConsumer.Builder builder = mUidBatteryConsumerBuilders.get(uid);
+ if (builder == null) {
+ builder = new UidBatteryConsumer.Builder(mCustomPowerComponentNames,
+ mIncludePowerModels, uid);
+ mUidBatteryConsumerBuilders.put(uid, builder);
+ }
+ return builder;
+ }
+
+ /**
* Creates or returns a UserBatteryConsumer, which represents battery attribution
* data for an individual {@link UserHandle}.
*/
@@ -756,5 +914,59 @@
public SparseArray<UidBatteryConsumer.Builder> getUidBatteryConsumerBuilders() {
return mUidBatteryConsumerBuilders;
}
+
+ /**
+ * Adds battery usage stats from another snapshots. The two snapshots are assumed to be
+ * non-overlapping, meaning that the power consumption estimates and session durations
+ * can be simply summed across the two snapshots. This remains true even if the timestamps
+ * seem to indicate that the sessions are in fact overlapping: timestamps may be off as a
+ * result of realtime clock adjustments by the user or the system.
+ */
+ @NonNull
+ public Builder add(BatteryUsageStats stats) {
+ if (!Arrays.equals(mCustomPowerComponentNames, stats.mCustomPowerComponentNames)) {
+ throw new IllegalArgumentException(
+ "BatteryUsageStats have different custom power components");
+ }
+
+ if (mUserBatteryConsumerBuilders.size() != 0
+ || !stats.getUserBatteryConsumers().isEmpty()) {
+ throw new UnsupportedOperationException(
+ "Combining UserBatteryConsumers is not supported");
+ }
+
+ mDischargedPowerLowerBoundMah += stats.mDischargedPowerLowerBound;
+ mDischargedPowerUpperBoundMah += stats.mDischargedPowerUpperBound;
+ mDischargePercentage += stats.mDischargePercentage;
+
+ mStatsDurationMs = getStatsDuration() + stats.getStatsDuration();
+
+ if (mStatsStartTimestampMs == 0
+ || stats.mStatsStartTimestampMs < mStatsStartTimestampMs) {
+ mStatsStartTimestampMs = stats.mStatsStartTimestampMs;
+ }
+
+ final boolean addingLaterSnapshot = stats.mStatsEndTimestampMs > mStatsEndTimestampMs;
+ if (addingLaterSnapshot) {
+ mStatsEndTimestampMs = stats.mStatsEndTimestampMs;
+ }
+
+ for (int scope = 0; scope < AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT; scope++) {
+ getAggregateBatteryConsumerBuilder(scope)
+ .add(stats.mAggregateBatteryConsumers[scope]);
+ }
+
+ for (UidBatteryConsumer consumer : stats.getUidBatteryConsumers()) {
+ getOrCreateUidBatteryConsumerBuilder(consumer.getUid()).add(consumer);
+ }
+
+ if (addingLaterSnapshot) {
+ mBatteryCapacityMah = stats.mBatteryCapacityMah;
+ mBatteryTimeRemainingMs = stats.mBatteryTimeRemainingMs;
+ mChargeTimeRemainingMs = stats.mChargeTimeRemainingMs;
+ }
+
+ return this;
+ }
}
}
diff --git a/core/java/android/os/BatteryUsageStatsQuery.java b/core/java/android/os/BatteryUsageStatsQuery.java
index 5080442..97f24cc 100644
--- a/core/java/android/os/BatteryUsageStatsQuery.java
+++ b/core/java/android/os/BatteryUsageStatsQuery.java
@@ -72,12 +72,16 @@
@NonNull
private final int[] mUserIds;
private final long mMaxStatsAgeMs;
+ private long mFromTimestamp;
+ private long mToTimestamp;
private BatteryUsageStatsQuery(@NonNull Builder builder) {
mFlags = builder.mFlags;
mUserIds = builder.mUserIds != null ? builder.mUserIds.toArray()
: new int[]{UserHandle.USER_ALL};
mMaxStatsAgeMs = builder.mMaxStatsAgeMs;
+ mFromTimestamp = builder.mFromTimestamp;
+ mToTimestamp = builder.mToTimestamp;
}
@BatteryUsageStatsFlags
@@ -112,11 +116,30 @@
return mMaxStatsAgeMs;
}
+ /**
+ * Returns the exclusive lower bound of the stored snapshot timestamps that should be included
+ * in the aggregation. Ignored if {@link #getToTimestamp()} is zero.
+ */
+ public long getFromTimestamp() {
+ return mFromTimestamp;
+ }
+
+ /**
+ * Returns the inclusive upper bound of the stored snapshot timestamps that should
+ * be included in the aggregation. The default is to include only the current stats
+ * accumulated since the latest battery reset.
+ */
+ public long getToTimestamp() {
+ return mToTimestamp;
+ }
+
private BatteryUsageStatsQuery(Parcel in) {
mFlags = in.readInt();
mUserIds = new int[in.readInt()];
in.readIntArray(mUserIds);
mMaxStatsAgeMs = in.readLong();
+ mFromTimestamp = in.readLong();
+ mToTimestamp = in.readLong();
}
@Override
@@ -125,6 +148,8 @@
dest.writeInt(mUserIds.length);
dest.writeIntArray(mUserIds);
dest.writeLong(mMaxStatsAgeMs);
+ dest.writeLong(mFromTimestamp);
+ dest.writeLong(mToTimestamp);
}
@Override
@@ -153,6 +178,8 @@
private int mFlags;
private IntArray mUserIds;
private long mMaxStatsAgeMs = DEFAULT_MAX_STATS_AGE_MS;
+ private long mFromTimestamp;
+ private long mToTimestamp;
/**
* Builds a read-only BatteryUsageStatsQuery object.
@@ -204,6 +231,17 @@
}
/**
+ * Requests to aggregate stored snapshots between the two supplied timestamps
+ * @param fromTimestamp Exclusive starting timestamp, as per System.currentTimeMillis()
+ * @param toTimestamp Inclusive ending timestamp, as per System.currentTimeMillis()
+ */
+ public Builder aggregateSnapshots(long fromTimestamp, long toTimestamp) {
+ mFromTimestamp = fromTimestamp;
+ mToTimestamp = toTimestamp;
+ return this;
+ }
+
+ /**
* Set the client's tolerance for stale battery stats. The data may be up to
* this many milliseconds out-of-date.
*/
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index a5b7e99..d7851a5 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1351,7 +1351,11 @@
public static final boolean IS_USER = "user".equals(TYPE);
/**
- * Whether this build is running inside a container.
+ * Whether this build is running on ARC, the Android Runtime for Chrome
+ * (https://chromium.googlesource.com/chromiumos/docs/+/master/containers_and_vms.md).
+ * Prior to R this was implemented as a container but from R this will be
+ * a VM. The name of the property remains ro.boot.conntainer as it is
+ * referenced in other projects.
*
* We should try to avoid checking this flag if possible to minimize
* unnecessarily diverging from non-container Android behavior.
@@ -1362,7 +1366,7 @@
* For higher-level behavior differences, other checks should be preferred.
* @hide
*/
- public static final boolean IS_CONTAINER =
+ public static final boolean IS_ARC =
SystemProperties.getBoolean("ro.boot.container", false);
/**
diff --git a/core/java/android/os/PersistableBundle.java b/core/java/android/os/PersistableBundle.java
index e5e9b5f..cad6e66 100644
--- a/core/java/android/os/PersistableBundle.java
+++ b/core/java/android/os/PersistableBundle.java
@@ -316,7 +316,7 @@
new MyReadMapCallback()));
}
}
- return EMPTY;
+ return new PersistableBundle(); // An empty mutable PersistableBundle
}
@Override
diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java
index a90ed20..db3d13b 100644
--- a/core/java/android/os/PowerComponents.java
+++ b/core/java/android/os/PowerComponents.java
@@ -19,11 +19,18 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.proto.ProtoOutputStream;
import com.android.internal.os.PowerCalculator;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
import java.io.PrintWriter;
+import java.util.Arrays;
/**
* Contains details of battery attribution data broken down to individual power drain types
@@ -36,9 +43,12 @@
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
private final double mConsumedPowerMah;
+ @NonNull
private final double[] mPowerComponentsMah;
+ @NonNull
private final long[] mUsageDurationsMs;
private final int mCustomPowerComponentCount;
+ @Nullable
private final byte[] mPowerModels;
// Not written to Parcel and must be explicitly restored during the parent object's unparceling
private String[] mCustomPowerComponentNames;
@@ -49,7 +59,7 @@
mPowerComponentsMah = builder.mPowerComponentsMah;
mUsageDurationsMs = builder.mUsageDurationsMs;
mConsumedPowerMah = builder.getTotalPower();
- mPowerModels = builder.mPowerModels;
+ mPowerModels = builder.getPowerModels();
}
PowerComponents(@NonNull Parcel source) {
@@ -146,9 +156,13 @@
}
}
+ public boolean hasPowerModels() {
+ return mPowerModels != null;
+ }
+
@BatteryConsumer.PowerModel
int getPowerModel(@BatteryConsumer.PowerComponent int component) {
- if (mPowerModels == null) {
+ if (!hasPowerModels()) {
throw new IllegalStateException(
"Power model IDs were not requested in the BatteryUsageStatsQuery");
}
@@ -294,10 +308,128 @@
return interestingData;
}
+ void writeToXml(TypedXmlSerializer serializer) throws IOException {
+ serializer.startTag(null, BatteryUsageStats.XML_TAG_POWER_COMPONENTS);
+ for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT;
+ componentId++) {
+ final double powerMah = getConsumedPower(componentId);
+ final long durationMs = getUsageDurationMillis(componentId);
+ if (powerMah == 0 && durationMs == 0) {
+ continue;
+ }
+
+ serializer.startTag(null, BatteryUsageStats.XML_TAG_COMPONENT);
+ serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_ID, componentId);
+ if (powerMah != 0) {
+ serializer.attributeDouble(null, BatteryUsageStats.XML_ATTR_POWER, powerMah);
+ }
+ if (durationMs != 0) {
+ serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_DURATION, durationMs);
+ }
+ if (mPowerModels != null) {
+ serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_MODEL,
+ mPowerModels[componentId]);
+ }
+ serializer.endTag(null, BatteryUsageStats.XML_TAG_COMPONENT);
+ }
+
+ final int customComponentEnd =
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + mCustomPowerComponentCount;
+ for (int componentId = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
+ componentId < customComponentEnd;
+ componentId++) {
+ final double powerMah = getConsumedPowerForCustomComponent(componentId);
+ final long durationMs = getUsageDurationForCustomComponentMillis(componentId);
+ if (powerMah == 0 && durationMs == 0) {
+ continue;
+ }
+
+ serializer.startTag(null, BatteryUsageStats.XML_TAG_CUSTOM_COMPONENT);
+ serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_ID, componentId);
+ if (powerMah != 0) {
+ serializer.attributeDouble(null, BatteryUsageStats.XML_ATTR_POWER, powerMah);
+ }
+ if (durationMs != 0) {
+ serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_DURATION, durationMs);
+ }
+ serializer.endTag(null, BatteryUsageStats.XML_TAG_CUSTOM_COMPONENT);
+ }
+
+ serializer.endTag(null, BatteryUsageStats.XML_TAG_POWER_COMPONENTS);
+ }
+
+
+ static void parseXml(TypedXmlPullParser parser, PowerComponents.Builder builder)
+ throws XmlPullParserException, IOException {
+ int eventType = parser.getEventType();
+ if (eventType != XmlPullParser.START_TAG || !parser.getName().equals(
+ BatteryUsageStats.XML_TAG_POWER_COMPONENTS)) {
+ throw new XmlPullParserException("Invalid XML parser state");
+ }
+
+ while (!(eventType == XmlPullParser.END_TAG && parser.getName().equals(
+ BatteryUsageStats.XML_TAG_POWER_COMPONENTS))
+ && eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG) {
+ switch (parser.getName()) {
+ case BatteryUsageStats.XML_TAG_COMPONENT: {
+ int componentId = -1;
+ double powerMah = 0;
+ long durationMs = 0;
+ int model = BatteryConsumer.POWER_MODEL_UNDEFINED;
+ for (int i = 0; i < parser.getAttributeCount(); i++) {
+ switch (parser.getAttributeName(i)) {
+ case BatteryUsageStats.XML_ATTR_ID:
+ componentId = parser.getAttributeInt(i);
+ break;
+ case BatteryUsageStats.XML_ATTR_POWER:
+ powerMah = parser.getAttributeDouble(i);
+ break;
+ case BatteryUsageStats.XML_ATTR_DURATION:
+ durationMs = parser.getAttributeLong(i);
+ break;
+ case BatteryUsageStats.XML_ATTR_MODEL:
+ model = parser.getAttributeInt(i);
+ break;
+ }
+ }
+ builder.setConsumedPower(componentId, powerMah, model);
+ builder.setUsageDurationMillis(componentId, durationMs);
+ break;
+ }
+ case BatteryUsageStats.XML_TAG_CUSTOM_COMPONENT: {
+ int componentId = -1;
+ double powerMah = 0;
+ long durationMs = 0;
+ for (int i = 0; i < parser.getAttributeCount(); i++) {
+ switch (parser.getAttributeName(i)) {
+ case BatteryUsageStats.XML_ATTR_ID:
+ componentId = parser.getAttributeInt(i);
+ break;
+ case BatteryUsageStats.XML_ATTR_POWER:
+ powerMah = parser.getAttributeDouble(i);
+ break;
+ case BatteryUsageStats.XML_ATTR_DURATION:
+ durationMs = parser.getAttributeLong(i);
+ break;
+ }
+ }
+ builder.setConsumedPowerForCustomComponent(componentId, powerMah);
+ builder.setUsageDurationForCustomComponentMillis(componentId, durationMs);
+ break;
+ }
+ }
+ }
+ eventType = parser.next();
+ }
+ }
+
/**
* Builder for PowerComponents.
*/
static final class Builder {
+ private static final byte POWER_MODEL_UNINITIALIZED = -1;
+
private final double[] mPowerComponentsMah;
private final String[] mCustomPowerComponentNames;
private final long[] mUsageDurationsMs;
@@ -311,6 +443,7 @@
mUsageDurationsMs = new long[powerComponentCount];
if (includePowerModels) {
mPowerModels = new byte[BatteryConsumer.POWER_COMPONENT_COUNT];
+ Arrays.fill(mPowerModels, POWER_MODEL_UNINITIALIZED);
} else {
mPowerModels = null;
}
@@ -412,12 +545,39 @@
return this;
}
- public void addPowerAndDuration(Builder other) {
+ public void addPowerAndDuration(PowerComponents.Builder other) {
+ addPowerAndDuration(other.mPowerComponentsMah, other.mUsageDurationsMs,
+ other.mPowerModels);
+ }
+
+ public void addPowerAndDuration(PowerComponents other) {
+ addPowerAndDuration(other.mPowerComponentsMah, other.mUsageDurationsMs,
+ other.mPowerModels);
+ }
+
+ private void addPowerAndDuration(double[] powerComponentsMah,
+ long[] usageDurationsMs, byte[] powerModels) {
+ if (mPowerComponentsMah.length != powerComponentsMah.length) {
+ throw new IllegalArgumentException(
+ "Number of power components does not match: " + powerComponentsMah.length
+ + ", expected: " + mPowerComponentsMah.length);
+ }
+
for (int i = mPowerComponentsMah.length - 1; i >= 0; i--) {
- mPowerComponentsMah[i] += other.mPowerComponentsMah[i];
+ mPowerComponentsMah[i] += powerComponentsMah[i];
}
for (int i = mUsageDurationsMs.length - 1; i >= 0; i--) {
- mUsageDurationsMs[i] += other.mUsageDurationsMs[i];
+ mUsageDurationsMs[i] += usageDurationsMs[i];
+ }
+ if (mPowerModels != null && powerModels != null) {
+ for (int i = mPowerModels.length - 1; i >= 0; i--) {
+ if (mPowerModels[i] == POWER_MODEL_UNINITIALIZED) {
+ mPowerModels[i] = powerModels[i];
+ } else if (mPowerModels[i] != powerModels[i]
+ && powerModels[i] != POWER_MODEL_UNINITIALIZED) {
+ mPowerModels[i] = BatteryConsumer.POWER_MODEL_UNDEFINED;
+ }
+ }
}
}
@@ -433,6 +593,19 @@
return totalPowerMah;
}
+ private byte[] getPowerModels() {
+ if (mPowerModels == null) {
+ return null;
+ }
+
+ byte[] powerModels = new byte[mPowerModels.length];
+ for (int i = mPowerModels.length - 1; i >= 0; i--) {
+ powerModels[i] = mPowerModels[i] != POWER_MODEL_UNINITIALIZED ? mPowerModels[i]
+ : BatteryConsumer.POWER_MODEL_UNDEFINED;
+ }
+ return powerModels;
+ }
+
/**
* Creates a read-only object out of the Builder values.
*/
diff --git a/core/java/android/os/SharedMemory.java b/core/java/android/os/SharedMemory.java
index 136e3de..f9b1695 100644
--- a/core/java/android/os/SharedMemory.java
+++ b/core/java/android/os/SharedMemory.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.system.ErrnoException;
import android.system.Os;
@@ -93,6 +94,18 @@
}
}
+ /**
+ * Creates from existing shared memory passed as {@link ParcelFileDesciptor}.
+ *
+ * @param fd File descriptor of shared memory passed as {@link #ParcelFileDescriptor}.
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static @NonNull SharedMemory create(@NonNull ParcelFileDescriptor fd) {
+ return new SharedMemory(fd.getFileDescriptor());
+ }
+
private static final int PROT_MASK = OsConstants.PROT_READ | OsConstants.PROT_WRITE
| OsConstants.PROT_EXEC | OsConstants.PROT_NONE;
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 44c3d61..7455f1f 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -115,20 +115,18 @@
*
* <pre>
* public void onCreate() {
- * if (DEVELOPER_MODE) {
- * StrictMode.setThreadPolicy(new {@link ThreadPolicy.Builder StrictMode.ThreadPolicy.Builder}()
- * .detectDiskReads()
- * .detectDiskWrites()
- * .detectNetwork() // or .detectAll() for all detectable problems
- * .penaltyLog()
- * .build());
- * StrictMode.setVmPolicy(new {@link VmPolicy.Builder StrictMode.VmPolicy.Builder}()
- * .detectLeakedSqlLiteObjects()
- * .detectLeakedClosableObjects()
- * .penaltyLog()
- * .penaltyDeath()
- * .build());
- * }
+ * StrictMode.setThreadPolicy(new {@link ThreadPolicy.Builder StrictMode.ThreadPolicy.Builder}()
+ * .detectDiskReads()
+ * .detectDiskWrites()
+ * .detectNetwork() // or .detectAll() for all detectable problems
+ * .penaltyLog()
+ * .build());
+ * StrictMode.setVmPolicy(new {@link VmPolicy.Builder StrictMode.VmPolicy.Builder}()
+ * .detectLeakedSqlLiteObjects()
+ * .detectLeakedClosableObjects()
+ * .penaltyLog()
+ * .penaltyDeath()
+ * .build());
* super.onCreate();
* }
* </pre>
@@ -147,9 +145,7 @@
* <p class="note">StrictMode is not a security mechanism and is not guaranteed to find all disk or
* network accesses. While it does propagate its state across process boundaries when doing {@link
* android.os.Binder} calls, it's still ultimately a best effort mechanism. Notably, disk or network
- * access from JNI calls won't necessarily trigger it. Future versions of Android may catch more (or
- * fewer) operations, so you should never leave StrictMode enabled in applications distributed on
- * Google Play.
+ * access from JNI calls won't necessarily trigger it.
*/
public final class StrictMode {
private static final String TAG = "StrictMode";
diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java
index 16a6c76..bfc4f73 100644
--- a/core/java/android/os/UidBatteryConsumer.java
+++ b/core/java/android/os/UidBatteryConsumer.java
@@ -19,9 +19,16 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.text.TextUtils;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import com.android.internal.os.PowerCalculator;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -143,13 +150,65 @@
return 0;
}
+ /** Serializes this object to XML */
+ void writeToXml(TypedXmlSerializer serializer) throws IOException {
+ if (getConsumedPower() == 0) {
+ return;
+ }
+
+ serializer.startTag(null, BatteryUsageStats.XML_TAG_UID);
+ serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_UID, getUid());
+ if (!TextUtils.isEmpty(mPackageWithHighestDrain)) {
+ serializer.attribute(null, BatteryUsageStats.XML_ATTR_HIGHEST_DRAIN_PACKAGE,
+ mPackageWithHighestDrain);
+ }
+ serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND,
+ mTimeInForegroundMs);
+ serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_BACKGROUND,
+ mTimeInBackgroundMs);
+ mPowerComponents.writeToXml(serializer);
+ serializer.endTag(null, BatteryUsageStats.XML_TAG_UID);
+ }
+
+ /** Parses an XML representation and populates the BatteryUsageStats builder */
+ static void createFromXml(TypedXmlPullParser parser, BatteryUsageStats.Builder builder)
+ throws XmlPullParserException, IOException {
+ final int uid = parser.getAttributeInt(null, BatteryUsageStats.XML_ATTR_UID);
+ final UidBatteryConsumer.Builder consumerBuilder =
+ builder.getOrCreateUidBatteryConsumerBuilder(uid);
+
+ int eventType = parser.getEventType();
+ if (eventType != XmlPullParser.START_TAG
+ || !parser.getName().equals(BatteryUsageStats.XML_TAG_UID)) {
+ throw new XmlPullParserException("Invalid XML parser state");
+ }
+
+ consumerBuilder.setPackageWithHighestDrain(
+ parser.getAttributeValue(null, BatteryUsageStats.XML_ATTR_HIGHEST_DRAIN_PACKAGE));
+ consumerBuilder.setTimeInStateMs(STATE_FOREGROUND,
+ parser.getAttributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND));
+ consumerBuilder.setTimeInStateMs(STATE_BACKGROUND,
+ parser.getAttributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_BACKGROUND));
+ while (!(eventType == XmlPullParser.END_TAG
+ && parser.getName().equals(BatteryUsageStats.XML_TAG_UID))
+ && eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG) {
+ if (parser.getName().equals(BatteryUsageStats.XML_TAG_POWER_COMPONENTS)) {
+ PowerComponents.parseXml(parser, consumerBuilder.mPowerComponentsBuilder);
+ }
+ }
+ eventType = parser.next();
+ }
+ }
+
/**
* Builder for UidBatteryConsumer.
*/
public static final class Builder extends BaseBuilder<Builder> {
+ private static final String PACKAGE_NAME_UNINITIALIZED = "";
private final BatteryStats.Uid mBatteryStatsUid;
private final int mUid;
- private String mPackageWithHighestDrain;
+ private String mPackageWithHighestDrain = PACKAGE_NAME_UNINITIALIZED;
public long mTimeInForegroundMs;
public long mTimeInBackgroundMs;
private boolean mExcludeFromBatteryUsageStats;
@@ -161,8 +220,19 @@
mUid = batteryStatsUid.getUid();
}
+ public Builder(@NonNull String[] customPowerComponentNames, boolean includePowerModels,
+ int uid) {
+ super(customPowerComponentNames, includePowerModels);
+ mBatteryStatsUid = null;
+ mUid = uid;
+ }
+
@NonNull
public BatteryStats.Uid getBatteryStatsUid() {
+ if (mBatteryStatsUid == null) {
+ throw new IllegalStateException(
+ "UidBatteryConsumer.Builder was initialized without a BatteryStats.Uid");
+ }
return mBatteryStatsUid;
}
@@ -176,7 +246,7 @@
*/
@NonNull
public Builder setPackageWithHighestDrain(@Nullable String packageName) {
- mPackageWithHighestDrain = packageName;
+ mPackageWithHighestDrain = TextUtils.nullIfEmpty(packageName);
return this;
}
@@ -208,6 +278,30 @@
}
/**
+ * Adds power and usage duration from the supplied UidBatteryConsumer.
+ */
+ public Builder add(UidBatteryConsumer consumer) {
+ mPowerComponentsBuilder.addPowerAndDuration(consumer.mPowerComponents);
+ mTimeInBackgroundMs += consumer.mTimeInBackgroundMs;
+ mTimeInForegroundMs += consumer.mTimeInForegroundMs;
+
+ if (mPackageWithHighestDrain == PACKAGE_NAME_UNINITIALIZED) {
+ mPackageWithHighestDrain = consumer.mPackageWithHighestDrain;
+ } else if (!TextUtils.equals(mPackageWithHighestDrain,
+ consumer.mPackageWithHighestDrain)) {
+ // Consider combining two UidBatteryConsumers with this distribution
+ // of power drain between packages:
+ // (package1=100, package2=10) and (package1=100, package2=101).
+ // Since we don't know the actual power distribution between packages at this
+ // point, we have no way to correctly declare package1 as the winner.
+ // The naive logic of picking the consumer with the higher total consumed
+ // power would produce an incorrect result.
+ mPackageWithHighestDrain = null;
+ }
+ return this;
+ }
+
+ /**
* Returns true if this UidBatteryConsumer must be excluded from the
* BatteryUsageStats.
*/
@@ -220,6 +314,9 @@
*/
@NonNull
public UidBatteryConsumer build() {
+ if (mPackageWithHighestDrain == PACKAGE_NAME_UNINITIALIZED) {
+ mPackageWithHighestDrain = null;
+ }
return new UidBatteryConsumer(this);
}
}
diff --git a/core/java/android/os/UserBatteryConsumer.java b/core/java/android/os/UserBatteryConsumer.java
index 429d2c5..b508a8c 100644
--- a/core/java/android/os/UserBatteryConsumer.java
+++ b/core/java/android/os/UserBatteryConsumer.java
@@ -17,9 +17,15 @@
package android.os;
import android.annotation.NonNull;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import com.android.internal.os.PowerCalculator;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -85,6 +91,42 @@
return 0;
}
+ /** Serializes this object to XML */
+ void writeToXml(TypedXmlSerializer serializer) throws IOException {
+ if (getConsumedPower() == 0) {
+ return;
+ }
+
+ serializer.startTag(null, BatteryUsageStats.XML_TAG_USER);
+ serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_USER_ID, getUserId());
+ mPowerComponents.writeToXml(serializer);
+ serializer.endTag(null, BatteryUsageStats.XML_TAG_USER);
+ }
+
+ /** Parses an XML representation and populates the BatteryUsageStats builder */
+ static void createFromXml(TypedXmlPullParser parser, BatteryUsageStats.Builder builder)
+ throws XmlPullParserException, IOException {
+ final int userId = parser.getAttributeInt(null, BatteryUsageStats.XML_ATTR_USER_ID);
+ final UserBatteryConsumer.Builder consumerBuilder =
+ builder.getOrCreateUserBatteryConsumerBuilder(userId);
+
+ int eventType = parser.getEventType();
+ if (eventType != XmlPullParser.START_TAG
+ || !parser.getName().equals(BatteryUsageStats.XML_TAG_USER)) {
+ throw new XmlPullParserException("Invalid XML parser state");
+ }
+ while (!(eventType == XmlPullParser.END_TAG
+ && parser.getName().equals(BatteryUsageStats.XML_TAG_USER))
+ && eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG) {
+ if (parser.getName().equals(BatteryUsageStats.XML_TAG_POWER_COMPONENTS)) {
+ PowerComponents.parseXml(parser, consumerBuilder.mPowerComponentsBuilder);
+ }
+ }
+ eventType = parser.next();
+ }
+ }
+
/**
* Builder for UserBatteryConsumer.
*/
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 12538e6..802f0b0 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -327,6 +327,8 @@
/**
* Vibrate constantly for the specified period of time.
*
+ * <p>The app should be in foreground for the vibration to happen.</p>
+ *
* @param milliseconds The number of milliseconds to vibrate.
* @deprecated Use {@link #vibrate(VibrationEffect)} instead.
*/
@@ -339,6 +341,9 @@
/**
* Vibrate constantly for the specified period of time.
*
+ * <p>The app should be in foreground for the vibration to happen. Background apps should
+ * specify a ringtone, notification or alarm usage in order to vibrate.</p>
+ *
* @param milliseconds The number of milliseconds to vibrate.
* @param attributes {@link AudioAttributes} corresponding to the vibration. For example,
* specify {@link AudioAttributes#USAGE_ALARM} for alarm vibrations or
@@ -373,6 +378,8 @@
* to start the repeat, or -1 to disable repeating.
* </p>
*
+ * <p>The app should be in foreground for the vibration to happen.</p>
+ *
* @param pattern an array of longs of times for which to turn the vibrator on or off.
* @param repeat the index into pattern at which to repeat, or -1 if
* you don't want to repeat.
@@ -398,6 +405,9 @@
* to start the repeat, or -1 to disable repeating.
* </p>
*
+ * <p>The app should be in foreground for the vibration to happen. Background apps should
+ * specify a ringtone, notification or alarm usage in order to vibrate.</p>
+ *
* @param pattern an array of longs of times for which to turn the vibrator on or off.
* @param repeat the index into pattern at which to repeat, or -1 if
* you don't want to repeat.
@@ -425,11 +435,30 @@
}
}
+ /**
+ * Vibrate with a given effect.
+ *
+ * <p>The app should be in foreground for the vibration to happen.</p>
+ *
+ * @param vibe {@link VibrationEffect} describing the vibration to be performed.
+ */
@RequiresPermission(android.Manifest.permission.VIBRATE)
public void vibrate(VibrationEffect vibe) {
vibrate(vibe, null);
}
+ /**
+ * Vibrate with a given effect.
+ *
+ * <p>The app should be in foreground for the vibration to happen. Background apps should
+ * specify a ringtone, notification or alarm usage in order to vibrate.</p>
+ *
+ * @param vibe {@link VibrationEffect} describing the vibration to be performed.
+ * @param attributes {@link AudioAttributes} corresponding to the vibration. For example,
+ * specify {@link AudioAttributes#USAGE_ALARM} for alarm vibrations or
+ * {@link AudioAttributes#USAGE_NOTIFICATION_RINGTONE} for
+ * vibrations associated with incoming calls.
+ */
@RequiresPermission(android.Manifest.permission.VIBRATE)
public void vibrate(VibrationEffect vibe, AudioAttributes attributes) {
vibrate(Process.myUid(), mPackageName, vibe, null, attributes);
diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl
index 9385402..fbac954 100644
--- a/core/java/android/os/storage/IStorageManager.aidl
+++ b/core/java/android/os/storage/IStorageManager.aidl
@@ -198,9 +198,9 @@
void clearUserKeyAuth(int userId, int serialNumber, in byte[] token, in byte[] secret) = 88;
void fixupAppDir(in String path) = 89;
void disableAppDataIsolation(in String pkgName, int pid, int userId) = 90;
- void notifyAppIoBlocked(in String volumeUuid, int uid, int tid, int reason) = 91;
- void notifyAppIoResumed(in String volumeUuid, int uid, int tid, int reason) = 92;
- PendingIntent getManageSpaceActivityIntent(in String packageName, int requestCode) = 93;
- boolean isAppIoBlocked(in String volumeUuid, int uid, int tid, int reason) = 94;
- int getExternalStorageMountMode(int uid, in String packageName) = 95;
-}
+ PendingIntent getManageSpaceActivityIntent(in String packageName, int requestCode) = 91;
+ void notifyAppIoBlocked(in String volumeUuid, int uid, int tid, int reason) = 92;
+ void notifyAppIoResumed(in String volumeUuid, int uid, int tid, int reason) = 93;
+ int getExternalStorageMountMode(int uid, in String packageName) = 94;
+ boolean isAppIoBlocked(in String volumeUuid, int uid, int tid, int reason) = 95;
+}
\ No newline at end of file
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index 400b312..69a09fb 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -214,7 +214,7 @@
return AudioManager.getAudioProductStrategies().stream()
.map(strategy -> strategy.getVolumeGroupIdForAudioAttributes(
- AudioProductStrategy.sDefaultAttributes))
+ AudioProductStrategy.getDefaultAttributes()))
.filter(volumeGroupId -> volumeGroupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP)
.findFirst()
.orElse(AudioVolumeGroup.DEFAULT_VOLUME_GROUP);
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 376d942..b3d60a5 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -8594,6 +8594,12 @@
* <p>
* Contacts-specific settings for various {@link Account}'s.
* </p>
+ * <p>
+ * A settings entry for an account is created automatically when a raw contact or group
+ * is inserted that references it. Settings entries cannot be deleted as long as raw
+ * contacts or groups continue to reference it; in order to delete a settings entry all
+ * raw contacts and groups referencing the account must be deleted first.
+ * </p>
* <h2>Columns</h2>
* <table class="jd-sumtable">
* <tr>
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 431bf4c..d1bbbc0 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -432,6 +432,15 @@
public static final String NAMESPACE_STORAGE_NATIVE_BOOT = "storage_native_boot";
/**
+ * Namespace for swcodec native related features.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_SWCODEC_NATIVE = "swcodec_native";
+
+
+ /**
* Namespace for System UI related features.
*
* @hide
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 36bc3e7..f36098d 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10972,6 +10972,9 @@
* <li>{@link HdmiControlManager#POWER_CONTROL_MODE_TV} Upon going to sleep, device
* sends {@code <Standby>} to TV only. Upon waking up, device does not turn on the Audio
* system via {@code <System Audio Mode Request>}.</li>
+ * <li>{@link HdmiControlManager#POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM} Upon going to
+ * sleep, sends {@code <Standby>} to TV and Audio system. Upon waking up, device attempts
+ * to turn on the Audio system via {@code <System Audio Mode Request>}.</li>
* <li>{@link HdmiControlManager#POWER_CONTROL_MODE_BROADCAST} Upon going to sleep,
* device sends {@code <Standby>} to all devices in the network. Upon waking up, device
* attempts to turn on the Audio system via {@code <System Audio Mode Request>}.</li>
@@ -14892,6 +14895,33 @@
"max_sound_trigger_detection_service_ops_per_day";
/**
+ * Setting indicating the name of the Wear OS app package containing the device's sysui.
+ *
+ * @hide
+ */
+ public static final String CLOCKWORK_SYSUI_PACKAGE_NAME =
+ "clockwork_sysui_package_name";
+
+ /**
+ * Setting indicating the name of the main activity of the Wear OS sysui.
+ *
+ * @hide
+ */
+ public static final String CLOCKWORK_SYSUI_MAIN_ACTIVITY_NAME =
+ "clockwork_sysui_main_activity_name";
+
+ /**
+ * Setting to determine if the Clockwork Home application is ready.
+ *
+ * <p>
+ * Set to 1 when the Clockwork Home application has finished starting up.
+ * </p>
+ *
+ * @hide
+ */
+ public static final String CLOCKWORK_HOME_READY = "clockwork_home_ready";
+
+ /**
* Indicates whether aware is available in the current location.
* @hide
*/
@@ -14950,6 +14980,15 @@
public static final String ONE_HANDED_KEYGUARD_SIDE = "one_handed_keyguard_side";
/**
+ * Global settings that shouldn't be persisted.
+ *
+ * @hide
+ */
+ public static final String[] TRANSIENT_SETTINGS = {
+ CLOCKWORK_HOME_READY,
+ };
+
+ /**
* Keys we no longer back up under the current schema, but want to continue to
* process when restoring historical backup datasets.
*
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index f3a8b5d..387fc39 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -5343,6 +5343,13 @@
public static final String COLUMN_RCS_CONFIG = "rcs_config";
/**
+ * TelephonyProvider column name for device to device sharing status.
+ *
+ * @hide
+ */
+ public static final String COLUMN_D2D_STATUS_SHARING = "d2d_sharing_status";
+
+ /**
* TelephonyProvider column name for VoIMS provisioning. Default is 0.
* <P>Type: INTEGER </P>
*
@@ -5351,13 +5358,6 @@
public static final String COLUMN_VOIMS_OPT_IN_STATUS = "voims_opt_in_status";
/**
- * TelephonyProvider column name for device to device sharing status.
- *
- * @hide
- */
- public static final String COLUMN_D2D_STATUS_SHARING = "d2d_sharing_status";
-
- /**
* TelephonyProvider column name for information selected contacts that allow device to
* device sharing.
*
@@ -5365,5 +5365,6 @@
*/
public static final String COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS =
"d2d_sharing_contacts";
+
}
}
diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java
index 62becc5..af846b6 100644
--- a/core/java/android/service/autofill/FillRequest.java
+++ b/core/java/android/service/autofill/FillRequest.java
@@ -96,6 +96,8 @@
*/
public static final @RequestFlags int FLAG_VIEW_NOT_FOCUSED = 0x10;
+ // The flag value 0x20 has been defined in AutofillManager.
+
/** @hide */
public static final int INVALID_REQUEST_ID = Integer.MIN_VALUE;
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index 4fd36e5..91042bfa 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -91,7 +91,8 @@
= "android.service.notification.NotificationAssistantService";
/**
- * Data type: int, the feedback rating score provided by user
+ * Data type: int, the feedback rating score provided by user. The score can be any integer
+ * value depends on the experimental and feedback UX design.
*/
public static final String FEEDBACK_RATING = "feedback.rating";
@@ -129,7 +130,8 @@
* A notification was posted by an app. Called before post.
*
* <p>Note: this method is only called if you don't override
- * {@link #onNotificationEnqueued(StatusBarNotification, NotificationChannel)}.</p>
+ * {@link #onNotificationEnqueued(StatusBarNotification, NotificationChannel)} or
+ * {@link #onNotificationEnqueued(StatusBarNotification, NotificationChannel, RankingMap)}.</p>
*
* @param sbn the new notification
* @return an adjustment or null to take no action, within 200ms.
@@ -139,6 +141,9 @@
/**
* A notification was posted by an app. Called before post.
*
+ * <p>Note: this method is only called if you don't override
+ * {@link #onNotificationEnqueued(StatusBarNotification, NotificationChannel, RankingMap)}.</p>
+ *
* @param sbn the new notification
* @param channel the channel the notification was posted to
* @return an adjustment or null to take no action, within 200ms.
@@ -282,7 +287,7 @@
* @param key the notification key
* @param rankingMap The current ranking map that can be used to retrieve ranking information
* for active notifications.
- * @param feedback the feedback detail
+ * @param feedback the received feedback, such as {@link #FEEDBACK_RATING rating score}
*/
public void onNotificationFeedbackReceived(@NonNull String key, @NonNull RankingMap rankingMap,
@NonNull Bundle feedback) {
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index f0f0867..505f400 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -2351,7 +2351,10 @@
final int ellipsisStringLen = ellipsisString.length();
// Use the ellipsis string only if there are that at least as many characters to replace.
final boolean useEllipsisString = ellipsisCount >= ellipsisStringLen;
- for (int i = 0; i < ellipsisCount; i++) {
+ final int min = Math.max(0, start - ellipsisStart - lineStart);
+ final int max = Math.min(ellipsisCount, end - ellipsisStart - lineStart);
+
+ for (int i = min; i < max; i++) {
final char c;
if (useEllipsisString && i < ellipsisStringLen) {
c = ellipsisString.charAt(i);
@@ -2360,9 +2363,7 @@
}
final int a = i + ellipsisStart + lineStart;
- if (start <= a && a < end) {
- dest[destoff + a - start] = c;
- }
+ dest[destoff + a - start] = c;
}
}
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index 696271c..f0ce325 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -23,10 +23,10 @@
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
-import android.content.pm.PackageParser;
import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
+import android.content.pm.SigningDetails.SignatureSchemeVersion;
import android.content.pm.parsing.ParsingPackageUtils;
import android.os.Build;
import android.os.Trace;
@@ -65,7 +65,7 @@
*
* @throws PackageParserException if the APK's signature failed to verify.
*/
- public static PackageParser.SigningDetails verify(String apkPath,
+ public static SigningDetails verify(String apkPath,
@SignatureSchemeVersion int minSignatureSchemeVersion)
throws PackageParserException {
return verifySignatures(apkPath, minSignatureSchemeVersion, true);
@@ -78,7 +78,7 @@
*
* @throws PackageParserException if there was a problem collecting certificates.
*/
- public static PackageParser.SigningDetails unsafeGetCertsWithoutVerification(
+ public static SigningDetails unsafeGetCertsWithoutVerification(
String apkPath, int minSignatureSchemeVersion)
throws PackageParserException {
return verifySignatures(apkPath, minSignatureSchemeVersion, false);
@@ -90,7 +90,7 @@
* @param verifyFull whether to verify all contents of this APK or just collect certificates.
* @throws PackageParserException if there was a problem collecting certificates
*/
- private static PackageParser.SigningDetails verifySignatures(String apkPath,
+ private static SigningDetails verifySignatures(String apkPath,
@SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull)
throws PackageParserException {
return verifySignaturesInternal(apkPath, minSignatureSchemeVersion,
@@ -249,7 +249,7 @@
}
}
- return new SigningDetailsWithDigests(new PackageParser.SigningDetails(signerSigs,
+ return new SigningDetailsWithDigests(new SigningDetails(signerSigs,
SignatureSchemeVersion.SIGNING_BLOCK_V4), vSigner.contentDigests);
} catch (SignatureNotFoundException e) {
throw e;
@@ -290,7 +290,7 @@
pastSignerSigs[i].setFlags(vSigner.por.flagsList.get(i));
}
}
- return new SigningDetailsWithDigests(new PackageParser.SigningDetails(signerSigs,
+ return new SigningDetailsWithDigests(new SigningDetails(signerSigs,
SignatureSchemeVersion.SIGNING_BLOCK_V3, pastSignerSigs),
vSigner.contentDigests);
} catch (SignatureNotFoundException e) {
@@ -321,7 +321,7 @@
ApkSignatureSchemeV2Verifier.verify(apkPath, verifyFull);
Certificate[][] signerCerts = vSigner.certs;
Signature[] signerSigs = convertToSignatures(signerCerts);
- return new SigningDetailsWithDigests(new PackageParser.SigningDetails(signerSigs,
+ return new SigningDetailsWithDigests(new SigningDetails(signerSigs,
SignatureSchemeVersion.SIGNING_BLOCK_V2), vSigner.contentDigests);
} catch (SignatureNotFoundException e) {
throw e;
@@ -408,7 +408,7 @@
}
}
return new SigningDetailsWithDigests(
- new PackageParser.SigningDetails(lastSigs, SignatureSchemeVersion.JAR), null);
+ new SigningDetails(lastSigs, SignatureSchemeVersion.JAR), null);
} catch (GeneralSecurityException e) {
throw new PackageParserException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING,
"Failed to collect certificates from " + apkPath, e);
@@ -565,7 +565,7 @@
* @hide for internal use only.
*/
public static class SigningDetailsWithDigests {
- public final PackageParser.SigningDetails signingDetails;
+ public final SigningDetails signingDetails;
/**
* APK Signature Schemes v2/v3/v4 might contain multiple content digests.
@@ -576,7 +576,7 @@
*/
public final Map<Integer, byte[]> contentDigests;
- SigningDetailsWithDigests(PackageParser.SigningDetails signingDetails,
+ SigningDetailsWithDigests(SigningDetails signingDetails,
Map<Integer, byte[]> contentDigests) {
this.signingDetails = signingDetails;
this.contentDigests = contentDigests;
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index f34cd8f..79f8a2a 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -162,6 +162,8 @@
IBinder displayToken, long nativeSurfaceObject);
private static native void nativeSetDisplayLayerStack(long transactionObj,
IBinder displayToken, int layerStack);
+ private static native void nativeSetDisplayFlags(long transactionObj,
+ IBinder displayToken, int flags);
private static native void nativeSetDisplayProjection(long transactionObj,
IBinder displayToken, int orientation,
int l, int t, int r, int b,
@@ -544,6 +546,15 @@
*/
private static final int SURFACE_OPAQUE = 0x02;
+ /* flags used with setDisplayFlags() (keep in sync with DisplayDevice.h) */
+
+ /**
+ * DisplayDevice flag: This display's transform is sent to inputflinger and used for input
+ * dispatch. This flag is used to disambiguate displays which share a layerstack.
+ * @hide
+ */
+ public static final int DISPLAY_RECEIVES_INPUT = 0x01;
+
// Display power modes.
/**
* Display power mode off: used while blanking the screen.
@@ -3173,6 +3184,17 @@
/**
* @hide
*/
+ public Transaction setDisplayFlags(IBinder displayToken, int flags) {
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+ nativeSetDisplayFlags(mNativeObject, displayToken, flags);
+ return this;
+ }
+
+ /**
+ * @hide
+ */
public Transaction setDisplayProjection(IBinder displayToken,
int orientation, Rect layerStackRect, Rect displayRect) {
if (displayToken == null) {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 6b0bb9d..94df402 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1554,12 +1554,21 @@
* @hide
*/
public void setResizeBackgroundColor(int bgColor) {
+ setResizeBackgroundColor(mTmpTransaction, bgColor);
+ mTmpTransaction.apply();
+ }
+
+ /**
+ * Version of {@link #setResizeBackgroundColor(int)} that allows you to provide
+ * {@link SurfaceControl.Transaction}.
+ * @hide
+ */
+ public void setResizeBackgroundColor(@NonNull SurfaceControl.Transaction t, int bgColor) {
if (mBackgroundControl == null) {
return;
}
-
mBackgroundColor = bgColor;
- updateBackgroundColor(mTmpTransaction).apply();
+ updateBackgroundColor(t);
}
@UnsupportedAppUsage
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 679da31..488f7c3 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2723,7 +2723,7 @@
continue;
}
childWithAccessibilityFocus = null;
- i = childrenCount - 1;
+ i = childrenCount;
}
if (!child.canReceivePointerEvents()
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index c1e394d..52812ef 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -467,6 +467,13 @@
int TRANSIT_FLAG_KEYGUARD_LOCKED = 0x40;
/**
+ * Transition flag: Indicates that this transition is for recents animation.
+ * TODO(b/188669821): Remove once special-case logic moves to shell.
+ * @hide
+ */
+ int TRANSIT_FLAG_IS_RECENTS = 0x80;
+
+ /**
* @hide
*/
@IntDef(flag = true, prefix = { "TRANSIT_FLAG_" }, value = {
@@ -476,7 +483,8 @@
TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION,
TRANSIT_FLAG_APP_CRASHED,
TRANSIT_FLAG_OPEN_BEHIND,
- TRANSIT_FLAG_KEYGUARD_LOCKED
+ TRANSIT_FLAG_KEYGUARD_LOCKED,
+ TRANSIT_FLAG_IS_RECENTS
})
@Retention(RetentionPolicy.SOURCE)
@interface TransitionFlags {}
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index f6d6fde..40da86a 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -1298,6 +1298,7 @@
parcel.readList(record.mText, null);
record.mSourceWindowId = parcel.readInt();
record.mSourceNodeId = parcel.readLong();
+ record.mSourceDisplayId = parcel.readInt();
record.mSealed = (parcel.readInt() == 1);
}
@@ -1364,6 +1365,7 @@
parcel.writeList(record.mText);
parcel.writeInt(record.mSourceWindowId);
parcel.writeLong(record.mSourceNodeId);
+ parcel.writeInt(record.mSourceDisplayId);
parcel.writeInt(record.mSealed ? 1 : 0);
}
@@ -1402,6 +1404,7 @@
if (DEBUG) {
builder.append("; SourceWindowId: 0x").append(Long.toHexString(mSourceWindowId));
builder.append("; SourceNodeId: 0x").append(Long.toHexString(mSourceNodeId));
+ builder.append("; SourceDisplayId: ").append(mSourceDisplayId);
}
for (int i = 0; i < getRecordCount(); i++) {
builder.append(" Record ").append(i).append(":");
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index 272dfac..8d49569 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -16,6 +16,9 @@
package android.view.accessibility;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CLIENT;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK;
+
import android.accessibilityservice.IAccessibilityServiceConnection;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -86,6 +89,7 @@
public static final int NO_ID = -1;
public static final String CALL_STACK = "call_stack";
+ public static final String IGNORE_CALL_STACK = "ignore_call_stack";
private static final String LOG_TAG = "AccessibilityInteractionClient";
@@ -121,6 +125,12 @@
private volatile int mInteractionId = -1;
private volatile int mCallingUid = Process.INVALID_UID;
+ // call stack for IAccessibilityInteractionConnectionCallback APIs. These callback APIs are
+ // shared by multiple requests APIs in IAccessibilityServiceConnection. To correctly log the
+ // request API which triggers the callback, we log trace entries for callback after the
+ // request API thread waiting for the callback returns. To log the correct callback stack in
+ // the request API thread, we save the callback stack in this member variables.
+ private List<StackTraceElement> mCallStackOfCallback;
private AccessibilityNodeInfo mFindAccessibilityNodeInfoResult;
@@ -307,18 +317,30 @@
if (DEBUG) {
Log.i(LOG_TAG, "Window cache hit");
}
+ if (shouldTraceClient()) {
+ logTraceClient(connection, "getWindow cache",
+ "connectionId=" + connectionId + ";accessibilityWindowId="
+ + accessibilityWindowId + ";bypassCache=false");
+ }
return window;
}
if (DEBUG) {
Log.i(LOG_TAG, "Window cache miss");
}
}
+
final long identityToken = Binder.clearCallingIdentity();
try {
window = connection.getWindow(accessibilityWindowId);
} finally {
Binder.restoreCallingIdentity(identityToken);
}
+ if (shouldTraceClient()) {
+ logTraceClient(connection, "getWindow", "connectionId=" + connectionId
+ + ";accessibilityWindowId=" + accessibilityWindowId + ";bypassCache="
+ + bypassCache);
+ }
+
if (window != null) {
if (!bypassCache) {
sAccessibilityCache.addWindow(window);
@@ -368,6 +390,10 @@
if (DEBUG) {
Log.i(LOG_TAG, "Windows cache hit");
}
+ if (shouldTraceClient()) {
+ logTraceClient(
+ connection, "getWindows cache", "connectionId=" + connectionId);
+ }
return windows;
}
if (DEBUG) {
@@ -379,6 +405,9 @@
} finally {
Binder.restoreCallingIdentity(identityToken);
}
+ if (shouldTraceClient()) {
+ logTraceClient(connection, "getWindows", "connectionId=" + connectionId);
+ }
if (windows != null) {
sAccessibilityCache.setWindowsOnAllDisplays(windows);
return windows;
@@ -472,6 +501,15 @@
Log.i(LOG_TAG, "Node cache hit for "
+ idToString(accessibilityWindowId, accessibilityNodeId));
}
+ if (shouldTraceClient()) {
+ logTraceClient(connection,
+ "findAccessibilityNodeInfoByAccessibilityId cache",
+ "connectionId=" + connectionId + ";accessibilityWindowId="
+ + accessibilityWindowId + ";accessibilityNodeId="
+ + accessibilityNodeId + ";bypassCache=" + bypassCache
+ + ";prefetchFlags=" + prefetchFlags + ";arguments="
+ + arguments);
+ }
return cachedInfo;
}
if (DEBUG) {
@@ -488,6 +526,14 @@
prefetchFlags &= ~AccessibilityNodeInfo.FLAG_PREFETCH_MASK;
}
final int interactionId = mInteractionIdCounter.getAndIncrement();
+ if (shouldTraceClient()) {
+ logTraceClient(connection, "findAccessibilityNodeInfoByAccessibilityId",
+ "InteractionId:" + interactionId + "connectionId=" + connectionId
+ + ";accessibilityWindowId=" + accessibilityWindowId
+ + ";accessibilityNodeId=" + accessibilityNodeId + ";bypassCache="
+ + bypassCache + ";prefetchFlags=" + prefetchFlags + ";arguments="
+ + arguments);
+ }
final String[] packageNames;
final long identityToken = Binder.clearCallingIdentity();
try {
@@ -500,16 +546,10 @@
if (packageNames != null) {
AccessibilityNodeInfo info =
getFindAccessibilityNodeInfoResultAndClear(interactionId);
- if (mAccessibilityManager != null
- && mAccessibilityManager.isAccessibilityTracingEnabled()) {
- logTrace(connection, "findAccessibilityNodeInfoByAccessibilityId",
- "InteractionId:" + interactionId + ";Result: " + info
- + ";connectionId=" + connectionId
- + ";accessibilityWindowId="
- + accessibilityWindowId + ";accessibilityNodeId="
- + accessibilityNodeId + ";bypassCache=" + bypassCache
- + ";prefetchFlags=" + prefetchFlags
- + ";arguments=" + arguments);
+ if (shouldTraceCallback()) {
+ logTraceCallback(connection, "findAccessibilityNodeInfoByAccessibilityId",
+ "InteractionId:" + interactionId + ";connectionId="
+ + connectionId + ";Result: " + info);
}
if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_MASK) != 0
&& info != null) {
@@ -571,6 +611,14 @@
final String[] packageNames;
final long identityToken = Binder.clearCallingIdentity();
try {
+ if (shouldTraceClient()) {
+ logTraceClient(connection, "findAccessibilityNodeInfosByViewId",
+ "InteractionId=" + interactionId + ";connectionId=" + connectionId
+ + ";accessibilityWindowId=" + accessibilityWindowId
+ + ";accessibilityNodeId=" + accessibilityNodeId + ";viewId="
+ + viewId);
+ }
+
packageNames = connection.findAccessibilityNodeInfosByViewId(
accessibilityWindowId, accessibilityNodeId, viewId, interactionId, this,
Thread.currentThread().getId());
@@ -581,13 +629,10 @@
if (packageNames != null) {
List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
interactionId);
- if (mAccessibilityManager != null
- && mAccessibilityManager.isAccessibilityTracingEnabled()) {
- logTrace(connection, "findAccessibilityNodeInfosByViewId", "InteractionId="
- + interactionId + ":Result: " + infos + ";connectionId="
- + connectionId + ";accessibilityWindowId=" + accessibilityWindowId
- + ";accessibilityNodeId=" + accessibilityNodeId + ";viewId="
- + viewId);
+ if (shouldTraceCallback()) {
+ logTraceCallback(connection, "findAccessibilityNodeInfosByViewId",
+ "InteractionId=" + interactionId + ";connectionId=" + connectionId
+ + ":Result: " + infos);
}
if (infos != null) {
finalizeAndCacheAccessibilityNodeInfos(infos, connectionId,
@@ -630,6 +675,12 @@
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
final int interactionId = mInteractionIdCounter.getAndIncrement();
+ if (shouldTraceClient()) {
+ logTraceClient(connection, "findAccessibilityNodeInfosByText",
+ "InteractionId:" + interactionId + "connectionId=" + connectionId
+ + ";accessibilityWindowId=" + accessibilityWindowId
+ + ";accessibilityNodeId=" + accessibilityNodeId + ";text=" + text);
+ }
final String[] packageNames;
final long identityToken = Binder.clearCallingIdentity();
try {
@@ -643,12 +694,10 @@
if (packageNames != null) {
List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
interactionId);
- if (mAccessibilityManager != null
- && mAccessibilityManager.isAccessibilityTracingEnabled()) {
- logTrace(connection, "findAccessibilityNodeInfosByText", "InteractionId="
- + interactionId + ":Result: " + infos + ";connectionId="
- + connectionId + ";accessibilityWindowId=" + accessibilityWindowId
- + ";accessibilityNodeId=" + accessibilityNodeId + ";text=" + text);
+ if (shouldTraceCallback()) {
+ logTraceCallback(connection, "findAccessibilityNodeInfosByText",
+ "InteractionId=" + interactionId + ";connectionId=" + connectionId
+ + ";Result: " + infos);
}
if (infos != null) {
finalizeAndCacheAccessibilityNodeInfos(infos, connectionId,
@@ -690,6 +739,13 @@
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
final int interactionId = mInteractionIdCounter.getAndIncrement();
+ if (shouldTraceClient()) {
+ logTraceClient(connection, "findFocus",
+ "InteractionId:" + interactionId + "connectionId=" + connectionId
+ + ";accessibilityWindowId=" + accessibilityWindowId
+ + ";accessibilityNodeId=" + accessibilityNodeId + ";focusType="
+ + focusType);
+ }
final String[] packageNames;
final long identityToken = Binder.clearCallingIdentity();
try {
@@ -703,13 +759,9 @@
if (packageNames != null) {
AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
interactionId);
- if (mAccessibilityManager != null
- && mAccessibilityManager.isAccessibilityTracingEnabled()) {
- logTrace(connection, "findFocus", "InteractionId=" + interactionId
- + ":Result: " + info + ";connectionId=" + connectionId
- + ";accessibilityWindowId=" + accessibilityWindowId
- + ";accessibilityNodeId=" + accessibilityNodeId + ";focusType="
- + focusType);
+ if (shouldTraceCallback()) {
+ logTraceCallback(connection, "findFocus", "InteractionId=" + interactionId
+ + ";connectionId=" + connectionId + ";Result:" + info);
}
finalizeAndCacheAccessibilityNodeInfo(info, connectionId, false, packageNames);
return info;
@@ -747,6 +799,13 @@
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
final int interactionId = mInteractionIdCounter.getAndIncrement();
+ if (shouldTraceClient()) {
+ logTraceClient(connection, "focusSearch",
+ "InteractionId:" + interactionId + "connectionId=" + connectionId
+ + ";accessibilityWindowId=" + accessibilityWindowId
+ + ";accessibilityNodeId=" + accessibilityNodeId + ";direction="
+ + direction);
+ }
final String[] packageNames;
final long identityToken = Binder.clearCallingIdentity();
try {
@@ -761,13 +820,9 @@
AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
interactionId);
finalizeAndCacheAccessibilityNodeInfo(info, connectionId, false, packageNames);
- if (mAccessibilityManager != null
- && mAccessibilityManager.isAccessibilityTracingEnabled()) {
- logTrace(connection, "focusSearch", "InteractionId=" + interactionId
- + ":Result: " + info + ";connectionId=" + connectionId
- + ";accessibilityWindowId=" + accessibilityWindowId
- + ";accessibilityNodeId=" + accessibilityNodeId + ";direction="
- + direction);
+ if (shouldTraceCallback()) {
+ logTraceCallback(connection, "focusSearch", "InteractionId=" + interactionId
+ + ";connectionId=" + connectionId + ";Result:" + info);
}
return info;
}
@@ -803,6 +858,13 @@
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
final int interactionId = mInteractionIdCounter.getAndIncrement();
+ if (shouldTraceClient()) {
+ logTraceClient(connection, "performAccessibilityAction",
+ "InteractionId:" + interactionId + "connectionId=" + connectionId
+ + ";accessibilityWindowId=" + accessibilityWindowId
+ + ";accessibilityNodeId=" + accessibilityNodeId + ";action=" + action
+ + ";arguments=" + arguments);
+ }
final boolean success;
final long identityToken = Binder.clearCallingIdentity();
try {
@@ -816,13 +878,10 @@
if (success) {
final boolean result =
getPerformAccessibilityActionResultAndClear(interactionId);
- if (mAccessibilityManager != null
- && mAccessibilityManager.isAccessibilityTracingEnabled()) {
- logTrace(connection, "performAccessibilityAction", "InteractionId="
- + interactionId + ":Result: " + result + ";connectionId="
- + connectionId + ";accessibilityWindowId=" + accessibilityWindowId
- + ";accessibilityNodeId=" + accessibilityNodeId + ";action="
- + action + ";arguments=" + arguments);
+ if (shouldTraceCallback()) {
+ logTraceCallback(connection, "performAccessibilityAction",
+ "InteractionId=" + interactionId + ";connectionId=" + connectionId
+ + ";Result: " + result);
}
return result;
}
@@ -883,6 +942,8 @@
mFindAccessibilityNodeInfoResult = info;
mInteractionId = interactionId;
mCallingUid = Binder.getCallingUid();
+ mCallStackOfCallback = new ArrayList<StackTraceElement>(
+ Arrays.asList(Thread.currentThread().getStackTrace()));
}
mInstanceLock.notifyAll();
}
@@ -933,6 +994,8 @@
}
mInteractionId = interactionId;
mCallingUid = Binder.getCallingUid();
+ mCallStackOfCallback = new ArrayList<StackTraceElement>(
+ Arrays.asList(Thread.currentThread().getStackTrace()));
}
mInstanceLock.notifyAll();
}
@@ -972,13 +1035,15 @@
finalizeAndCacheAccessibilityNodeInfos(
infos, connectionIdWaitingForPrefetchResultCopy, false,
packageNamesForNextPrefetchResultCopy);
- if (mAccessibilityManager != null
- && mAccessibilityManager.isAccessibilityTracingEnabled()) {
+ if (shouldTraceCallback()) {
logTrace(getConnection(connectionIdWaitingForPrefetchResultCopy),
"setPrefetchAccessibilityNodeInfoResult",
- "InteractionId:" + interactionId + ";Result: " + infos
- + ";connectionId=" + connectionIdWaitingForPrefetchResultCopy,
- Binder.getCallingUid());
+ "InteractionId:" + interactionId + ";connectionId="
+ + connectionIdWaitingForPrefetchResultCopy + ";Result: " + infos,
+ Binder.getCallingUid(),
+ Arrays.asList(Thread.currentThread().getStackTrace()),
+ new HashSet<String>(Arrays.asList("getStackTrace")),
+ FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK);
}
} else if (DEBUG) {
Log.w(LOG_TAG, "Prefetching for interaction with id " + interactionId + " dropped "
@@ -1010,6 +1075,8 @@
mPerformAccessibilityActionResult = succeeded;
mInteractionId = interactionId;
mCallingUid = Binder.getCallingUid();
+ mCallStackOfCallback = new ArrayList<StackTraceElement>(
+ Arrays.asList(Thread.currentThread().getStackTrace()));
}
mInstanceLock.notifyAll();
}
@@ -1219,24 +1286,45 @@
return true;
}
+ private boolean shouldTraceClient() {
+ return (mAccessibilityManager != null)
+ && mAccessibilityManager.isA11yInteractionClientTraceEnabled();
+ }
+
+ private boolean shouldTraceCallback() {
+ return (mAccessibilityManager != null)
+ && mAccessibilityManager.isA11yInteractionConnectionCBTraceEnabled();
+ }
+
private void logTrace(
IAccessibilityServiceConnection connection, String method, String params,
- int callingUid) {
+ int callingUid, List<StackTraceElement> callStack, HashSet<String> ignoreSet,
+ long logTypes) {
try {
Bundle b = new Bundle();
- ArrayList<StackTraceElement> callStack = new ArrayList<StackTraceElement>(
- Arrays.asList(Thread.currentThread().getStackTrace()));
- b.putSerializable(CALL_STACK, callStack);
+ b.putSerializable(CALL_STACK, new ArrayList<StackTraceElement>(callStack));
+ if (ignoreSet != null) {
+ b.putSerializable(IGNORE_CALL_STACK, ignoreSet);
+ }
connection.logTrace(SystemClock.elapsedRealtimeNanos(),
- LOG_TAG + ".callback for " + method, params, Process.myPid(),
- Thread.currentThread().getId(), callingUid, b);
+ LOG_TAG + "." + method,
+ logTypes, params, Process.myPid(), Thread.currentThread().getId(),
+ callingUid, b);
} catch (RemoteException e) {
Log.e(LOG_TAG, "Failed to log trace. " + e);
}
}
- private void logTrace(
+ private void logTraceCallback(
IAccessibilityServiceConnection connection, String method, String params) {
- logTrace(connection, method, params, mCallingUid);
+ logTrace(connection, method + " callback", params, mCallingUid, mCallStackOfCallback,
+ new HashSet<String>(Arrays.asList("getStackTrace")),
+ FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK);
+ }
+
+ private void logTraceClient(
+ IAccessibilityServiceConnection connection, String method, String params) {
+ logTrace(connection, method, params, Binder.getCallingUid(),
+ Collections.emptyList(), null, FLAGS_ACCESSIBILITY_INTERACTION_CLIENT);
}
}
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index f9cdbd3..17fad7e 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -111,7 +111,13 @@
public static final int STATE_FLAG_REQUEST_MULTI_FINGER_GESTURES = 0x00000010;
/** @hide */
- public static final int STATE_FLAG_ACCESSIBILITY_TRACING_ENABLED = 0x00000020;
+ public static final int STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_ENABLED = 0x00000100;
+ /** @hide */
+ public static final int STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_CB_ENABLED = 0x00000200;
+ /** @hide */
+ public static final int STATE_FLAG_TRACE_A11Y_INTERACTION_CLIENT_ENABLED = 0x00000400;
+ /** @hide */
+ public static final int STATE_FLAG_TRACE_A11Y_SERVICE_ENABLED = 0x00000800;
/** @hide */
public static final int DALTONIZER_DISABLED = -1;
@@ -235,8 +241,8 @@
@UnsupportedAppUsage(trackingBug = 123768939L)
boolean mIsHighTextContrastEnabled;
- // Whether accessibility tracing is enabled or not
- boolean mIsAccessibilityTracingEnabled = false;
+ // accessibility tracing state
+ int mAccessibilityTracingState = 0;
AccessibilityPolicy mAccessibilityPolicy;
@@ -1029,13 +1035,50 @@
}
/**
- * Gets accessibility tracing enabled state.
+ * Gets accessibility interaction connection tracing enabled state.
*
* @hide
*/
- public boolean isAccessibilityTracingEnabled() {
+ public boolean isA11yInteractionConnectionTraceEnabled() {
synchronized (mLock) {
- return mIsAccessibilityTracingEnabled;
+ return ((mAccessibilityTracingState
+ & STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_ENABLED) != 0);
+ }
+ }
+
+ /**
+ * Gets accessibility interaction connection callback tracing enabled state.
+ *
+ * @hide
+ */
+ public boolean isA11yInteractionConnectionCBTraceEnabled() {
+ synchronized (mLock) {
+ return ((mAccessibilityTracingState
+ & STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_CB_ENABLED) != 0);
+ }
+ }
+
+ /**
+ * Gets accessibility interaction client tracing enabled state.
+ *
+ * @hide
+ */
+ public boolean isA11yInteractionClientTraceEnabled() {
+ synchronized (mLock) {
+ return ((mAccessibilityTracingState
+ & STATE_FLAG_TRACE_A11Y_INTERACTION_CLIENT_ENABLED) != 0);
+ }
+ }
+
+ /**
+ * Gets accessibility service tracing enabled state.
+ *
+ * @hide
+ */
+ public boolean isA11yServiceTraceEnabled() {
+ synchronized (mLock) {
+ return ((mAccessibilityTracingState
+ & STATE_FLAG_TRACE_A11Y_SERVICE_ENABLED) != 0);
}
}
@@ -1233,8 +1276,6 @@
(stateFlags & STATE_FLAG_TOUCH_EXPLORATION_ENABLED) != 0;
final boolean highTextContrastEnabled =
(stateFlags & STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED) != 0;
- final boolean accessibilityTracingEnabled =
- (stateFlags & STATE_FLAG_ACCESSIBILITY_TRACING_ENABLED) != 0;
final boolean wasEnabled = isEnabled();
final boolean wasTouchExplorationEnabled = mIsTouchExplorationEnabled;
@@ -1257,7 +1298,7 @@
notifyHighTextContrastStateChanged();
}
- updateAccessibilityTracingState(accessibilityTracingEnabled);
+ updateAccessibilityTracingState(stateFlags);
}
/**
@@ -1715,11 +1756,11 @@
}
/**
- * Update mIsAccessibilityTracingEnabled.
+ * Update mAccessibilityTracingState.
*/
- private void updateAccessibilityTracingState(boolean enabled) {
+ private void updateAccessibilityTracingState(int stateFlag) {
synchronized (mLock) {
- mIsAccessibilityTracingEnabled = enabled;
+ mAccessibilityTracingState = stateFlag;
}
}
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index c3a4d32..f26abb2 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -20,8 +20,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcelable;
+import android.view.Display;
import android.view.View;
import java.util.ArrayList;
@@ -104,6 +106,7 @@
@UnsupportedAppUsage
long mSourceNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
int mSourceWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+ int mSourceDisplayId = Display.INVALID_DISPLAY;
CharSequence mClassName;
CharSequence mContentDescription;
@@ -202,6 +205,27 @@
}
/**
+ * Sets the display id.
+ *
+ * @param displayId The displayId id.
+ *
+ * @hide
+ */
+ @TestApi
+ public void setDisplayId(int displayId) {
+ mSourceDisplayId = displayId;
+ }
+
+ /**
+ * Gets the id of the display from which the event comes from.
+ *
+ * @return The display id.
+ */
+ public int getDisplayId() {
+ return mSourceDisplayId;
+ }
+
+ /**
* Sets the window id.
*
* @param windowId The window id.
@@ -886,6 +910,7 @@
mText.addAll(record.mText);
mSourceWindowId = record.mSourceWindowId;
mSourceNodeId = record.mSourceNodeId;
+ mSourceDisplayId = record.mSourceDisplayId;
mConnectionId = record.mConnectionId;
}
@@ -914,6 +939,7 @@
mText.clear();
mSourceNodeId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
mSourceWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+ mSourceDisplayId = Display.INVALID_DISPLAY;
mConnectionId = UNDEFINED;
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 4df8fd2..a169cb0 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -25,6 +25,7 @@
import static android.view.autofill.Helper.toList;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -45,16 +46,21 @@
import android.content.pm.ResolveInfo;
import android.graphics.Rect;
import android.metrics.LogMaker;
+import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
+import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
+import android.os.ICancellationSignal;
import android.os.Looper;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.SystemClock;
import android.service.autofill.AutofillService;
+import android.service.autofill.FillCallback;
import android.service.autofill.FillEventHistory;
+import android.service.autofill.IFillCallback;
import android.service.autofill.UserData;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -74,6 +80,7 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeProvider;
import android.view.accessibility.AccessibilityWindowInfo;
+import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.TextView;
@@ -99,6 +106,7 @@
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.Executor;
import sun.misc.Cleaner;
@@ -167,6 +175,12 @@
* shows an autofill save UI if the value of savable views have changed. If the user selects the
* option to Save, the current value of the views is then sent to the autofill service.
*
+ * <p>There is another choice for the application to provide it's datasets to the Autofill framework
+ * by setting an {@link AutofillRequestCallback} through
+ * {@link #setAutofillRequestCallback(Executor, AutofillRequestCallback)}. The application can use
+ * its callback instead of the default {@link AutofillService}. See
+ * {@link AutofillRequestCallback} for more details.
+ *
* <h3 id="additional-notes">Additional notes</h3>
*
* <p>It is safe to call <code>AutofillManager</code> methods from any thread.
@@ -292,6 +306,7 @@
/** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2;
/** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4;
/** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY = 0x8;
+ /** @hide */ public static final int FLAG_ENABLED_CLIENT_SUGGESTIONS = 0x20;
// NOTE: flag below is used by the session start receiver only, hence it can have values above
/** @hide */ public static final int RECEIVER_FLAG_SESSION_FOR_AUGMENTED_AUTOFILL_ONLY = 0x1;
@@ -592,6 +607,11 @@
@GuardedBy("mLock")
private boolean mEnabledForAugmentedAutofillOnly;
+ @GuardedBy("mLock")
+ @Nullable private AutofillRequestCallback mAutofillRequestCallback;
+ @GuardedBy("mLock")
+ @Nullable private Executor mRequestCallbackExecutor;
+
/** @hide */
public interface AutofillClient {
/**
@@ -1836,6 +1856,32 @@
return new AutofillId(parent.getAutofillViewId(), virtualId);
}
+ /**
+ * Sets the client's suggestions callback for autofill.
+ *
+ * @see AutofillRequestCallback
+ *
+ * @param executor specifies the thread upon which the callbacks will be invoked.
+ * @param callback which handles autofill request to provide client's suggestions.
+ */
+ public void setAutofillRequestCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull AutofillRequestCallback callback) {
+ synchronized (mLock) {
+ mRequestCallbackExecutor = executor;
+ mAutofillRequestCallback = callback;
+ }
+ }
+
+ /**
+ * clears the client's suggestions callback for autofill.
+ */
+ public void clearAutofillRequestCallback() {
+ synchronized (mLock) {
+ mRequestCallbackExecutor = null;
+ mAutofillRequestCallback = null;
+ }
+ }
+
@GuardedBy("mLock")
private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
@NonNull AutofillValue value, int flags) {
@@ -1896,6 +1942,13 @@
}
}
+ if (mAutofillRequestCallback != null) {
+ if (sDebug) {
+ Log.d(TAG, "startSession with the client suggestions provider");
+ }
+ flags |= FLAG_ENABLED_CLIENT_SUGGESTIONS;
+ }
+
mService.startSession(client.autofillClientGetActivityToken(),
mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
mCallback != null, flags, clientActivity,
@@ -2245,6 +2298,28 @@
}
}
+ private void onFillRequest(InlineSuggestionsRequest request,
+ CancellationSignal cancellationSignal, FillCallback callback) {
+ final AutofillRequestCallback autofillRequestCallback;
+ final Executor executor;
+ synchronized (mLock) {
+ autofillRequestCallback = mAutofillRequestCallback;
+ executor = mRequestCallbackExecutor;
+ }
+ if (autofillRequestCallback != null && executor != null) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() ->
+ autofillRequestCallback.onFillRequest(
+ request, cancellationSignal, callback));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ } else {
+ callback.onSuccess(null);
+ }
+ }
+
/** @hide */
public static final int SET_STATE_FLAG_ENABLED = 0x01;
/** @hide */
@@ -3624,6 +3699,23 @@
afm.post(() -> afm.requestShowSoftInput(id));
}
}
+
+ @Override
+ public void requestFillFromClient(int id, InlineSuggestionsRequest request,
+ IFillCallback callback) {
+ final AutofillManager afm = mAfm.get();
+ if (afm != null) {
+ ICancellationSignal transport = CancellationSignal.createTransport();
+ try {
+ callback.onCancellable(transport);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error requesting a cancellation", e);
+ }
+
+ afm.onFillRequest(request, CancellationSignal.fromTransport(transport),
+ new FillCallback(callback, id));
+ }
+ }
}
private static final class AugmentedAutofillManagerClient
diff --git a/core/java/android/view/autofill/AutofillRequestCallback.java b/core/java/android/view/autofill/AutofillRequestCallback.java
new file mode 100644
index 0000000..e632a58
--- /dev/null
+++ b/core/java/android/view/autofill/AutofillRequestCallback.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.autofill;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.CancellationSignal;
+import android.service.autofill.FillCallback;
+import android.view.inputmethod.InlineSuggestionsRequest;
+
+/**
+ * <p>This class is used to provide some input suggestions to the Autofill framework.
+ *
+ * <P>When the user is requested to input something, Autofill will try to query input suggestions
+ * for the user choosing. If the application want to provide some internal input suggestions,
+ * implements this callback and register via
+ * {@link AutofillManager#setAutofillRequestCallback(java.util.concurrent.Executor,
+ * AutofillRequestCallback)}. Autofill will callback the
+ * {@link #onFillRequest(InlineSuggestionsRequest, CancellationSignal, FillCallback)} to request
+ * input suggestions.
+ *
+ * <P>To make sure the callback to take effect, must register before the autofill session starts.
+ * If the autofill session is started, calls {@link AutofillManager#cancel()} to finish current
+ * session, and then the callback will be used at the next restarted session.
+ *
+ * <P>To create a {@link android.service.autofill.FillResponse}, application should fetch
+ * {@link AutofillId}s from its view structure. Below is an example:
+ * <pre class="prettyprint">
+ * AutofillId usernameId = findViewById(R.id.username).getAutofillId();
+ * AutofillId passwordId = findViewById(R.id.password).getAutofillId();
+ * </pre>
+ * To learn more about creating a {@link android.service.autofill.FillResponse}, read
+ * <a href="/guide/topics/text/autofill-services#fill">Fill out client views</a>.
+ *
+ * <P>To fallback to the default {@link android.service.autofill.AutofillService}, just respond
+ * a null of the {@link android.service.autofill.FillResponse}. And then Autofill will do a fill
+ * request with the default {@link android.service.autofill.AutofillService}. Or clear the callback
+ * from {@link AutofillManager} via {@link AutofillManager#clearAutofillRequestCallback()}. If the
+ * client would like to keep no suggestions for the field, respond with an empty
+ * {@link android.service.autofill.FillResponse} which has no dataset.
+ *
+ * <P>IMPORTANT: This should not be used for displaying anything other than input suggestions, or
+ * the keyboard may choose to block your app from the inline strip.
+ */
+public interface AutofillRequestCallback {
+ /**
+ * Called by the Android system to decide if a screen can be autofilled by the callback.
+ *
+ * @param inlineSuggestionsRequest the {@link InlineSuggestionsRequest request} to handle if
+ * currently inline suggestions are supported and can be displayed.
+ * @param cancellationSignal signal for observing cancellation requests. The system will use
+ * this to notify you that the fill result is no longer needed and you should stop
+ * handling this fill request in order to save resources.
+ * @param callback object used to notify the result of the request.
+ */
+ void onFillRequest(@Nullable InlineSuggestionsRequest inlineSuggestionsRequest,
+ @NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback);
+}
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index 1f833f6..64507aa 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -24,9 +24,11 @@
import android.content.IntentSender;
import android.graphics.Rect;
import android.os.IBinder;
+import android.service.autofill.IFillCallback;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
import android.view.autofill.IAutofillWindowPresenter;
+import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.KeyEvent;
import com.android.internal.os.IResultReceiver;
@@ -140,4 +142,10 @@
* Requests to show the soft input method if the focus is on the given id.
*/
void requestShowSoftInput(in AutofillId id);
+
+ /**
+ * Requests to determine if a screen can be autofilled by the client app.
+ */
+ void requestFillFromClient(int id, in InlineSuggestionsRequest request,
+ in IFillCallback callback);
}
diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
index 1eb1a93..e1e1755 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
@@ -111,6 +111,22 @@
private @Nullable InlinePresentationSpec mInlineTooltipPresentationSpec;
/**
+ * Whether the IME supports inline suggestions from the default Autofill service that
+ * provides the input view.
+ *
+ * Note: The default value is {@code true}.
+ */
+ private boolean mServiceSupported;
+
+ /**
+ * Whether the IME supports inline suggestions from the application that provides the
+ * input view.
+ *
+ * Note: The default value is {@code true}.
+ */
+ private boolean mClientSupported;
+
+ /**
* @hide
* @see {@link #mHostInputToken}.
*/
@@ -204,6 +220,14 @@
return Bundle.EMPTY;
}
+ private static boolean defaultServiceSupported() {
+ return true;
+ }
+
+ private static boolean defaultClientSupported() {
+ return true;
+ }
+
/** @hide */
abstract static class BaseBuilder {
abstract Builder setInlinePresentationSpecs(
@@ -216,15 +240,25 @@
abstract Builder setHostDisplayId(int value);
}
+ /** @hide */
+ public boolean isServiceSupported() {
+ return mServiceSupported;
+ }
+
+ /** @hide */
+ public boolean isClientSupported() {
+ return mClientSupported;
+ }
- // Code below generated by codegen v1.0.23.
+
+ // Code below generated by codegen v1.0.22.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/./frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
@@ -240,7 +274,9 @@
@NonNull Bundle extras,
@Nullable IBinder hostInputToken,
int hostDisplayId,
- @Nullable InlinePresentationSpec inlineTooltipPresentationSpec) {
+ @Nullable InlinePresentationSpec inlineTooltipPresentationSpec,
+ boolean serviceSupported,
+ boolean clientSupported) {
this.mMaxSuggestionCount = maxSuggestionCount;
this.mInlinePresentationSpecs = inlinePresentationSpecs;
com.android.internal.util.AnnotationValidations.validate(
@@ -257,6 +293,8 @@
this.mHostInputToken = hostInputToken;
this.mHostDisplayId = hostDisplayId;
this.mInlineTooltipPresentationSpec = inlineTooltipPresentationSpec;
+ this.mServiceSupported = serviceSupported;
+ this.mClientSupported = clientSupported;
onConstructed();
}
@@ -340,7 +378,9 @@
}
/**
- * Specifies the UI specification for the inline suggestion tooltip in the response.
+ * The {@link InlinePresentationSpec} for the inline suggestion tooltip in the response.
+ *
+ * @see android.service.autofill.InlinePresentation#createTooltipPresentation(Slice, InlinePresentationSpec)
*/
@DataClass.Generated.Member
public @Nullable InlinePresentationSpec getInlineTooltipPresentationSpec() {
@@ -361,7 +401,9 @@
"extras = " + mExtras + ", " +
"hostInputToken = " + mHostInputToken + ", " +
"hostDisplayId = " + mHostDisplayId + ", " +
- "inlineTooltipPresentationSpec = " + mInlineTooltipPresentationSpec +
+ "inlineTooltipPresentationSpec = " + mInlineTooltipPresentationSpec + ", " +
+ "serviceSupported = " + mServiceSupported + ", " +
+ "clientSupported = " + mClientSupported +
" }";
}
@@ -385,7 +427,9 @@
&& extrasEquals(that.mExtras)
&& java.util.Objects.equals(mHostInputToken, that.mHostInputToken)
&& mHostDisplayId == that.mHostDisplayId
- && java.util.Objects.equals(mInlineTooltipPresentationSpec, that.mInlineTooltipPresentationSpec);
+ && java.util.Objects.equals(mInlineTooltipPresentationSpec, that.mInlineTooltipPresentationSpec)
+ && mServiceSupported == that.mServiceSupported
+ && mClientSupported == that.mClientSupported;
}
@Override
@@ -403,6 +447,8 @@
_hash = 31 * _hash + java.util.Objects.hashCode(mHostInputToken);
_hash = 31 * _hash + mHostDisplayId;
_hash = 31 * _hash + java.util.Objects.hashCode(mInlineTooltipPresentationSpec);
+ _hash = 31 * _hash + Boolean.hashCode(mServiceSupported);
+ _hash = 31 * _hash + Boolean.hashCode(mClientSupported);
return _hash;
}
@@ -413,6 +459,8 @@
// void parcelFieldName(Parcel dest, int flags) { ... }
int flg = 0;
+ if (mServiceSupported) flg |= 0x100;
+ if (mClientSupported) flg |= 0x200;
if (mHostInputToken != null) flg |= 0x20;
if (mInlineTooltipPresentationSpec != null) flg |= 0x80;
dest.writeInt(flg);
@@ -438,6 +486,8 @@
// static FieldType unparcelFieldName(Parcel in) { ... }
int flg = in.readInt();
+ boolean serviceSupported = (flg & 0x100) != 0;
+ boolean clientSupported = (flg & 0x200) != 0;
int maxSuggestionCount = in.readInt();
List<InlinePresentationSpec> inlinePresentationSpecs = new ArrayList<>();
in.readParcelableList(inlinePresentationSpecs, InlinePresentationSpec.class.getClassLoader());
@@ -464,6 +514,8 @@
this.mHostInputToken = hostInputToken;
this.mHostDisplayId = hostDisplayId;
this.mInlineTooltipPresentationSpec = inlineTooltipPresentationSpec;
+ this.mServiceSupported = serviceSupported;
+ this.mClientSupported = clientSupported;
onConstructed();
}
@@ -497,6 +549,8 @@
private @Nullable IBinder mHostInputToken;
private int mHostDisplayId;
private @Nullable InlinePresentationSpec mInlineTooltipPresentationSpec;
+ private boolean mServiceSupported;
+ private boolean mClientSupported;
private long mBuilderFieldsSet = 0L;
@@ -629,7 +683,9 @@
}
/**
- * Specifies the UI specification for the inline suggestion tooltip in the response.
+ * The {@link InlinePresentationSpec} for the inline suggestion tooltip in the response.
+ *
+ * @see android.service.autofill.InlinePresentation#createTooltipPresentation(Slice, InlinePresentationSpec)s
*/
@DataClass.Generated.Member
public @NonNull Builder setInlineTooltipPresentationSpec(@NonNull InlinePresentationSpec value) {
@@ -639,10 +695,38 @@
return this;
}
+ /**
+ * Whether the IME supports inline suggestions from the default Autofill service that
+ * provides the input view.
+ *
+ * Note: The default value is {@code true}.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setServiceSupported(boolean value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x100;
+ mServiceSupported = value;
+ return this;
+ }
+
+ /**
+ * Whether the IME supports inline suggestions from the application that provides the
+ * input view.
+ *
+ * Note: The default value is {@code true}.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setClientSupported(boolean value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x200;
+ mClientSupported = value;
+ return this;
+ }
+
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull InlineSuggestionsRequest build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x100; // Mark builder used
+ mBuilderFieldsSet |= 0x400; // Mark builder used
if ((mBuilderFieldsSet & 0x1) == 0) {
mMaxSuggestionCount = defaultMaxSuggestionCount();
@@ -665,6 +749,12 @@
if ((mBuilderFieldsSet & 0x80) == 0) {
mInlineTooltipPresentationSpec = defaultInlineTooltipPresentationSpec();
}
+ if ((mBuilderFieldsSet & 0x100) == 0) {
+ mServiceSupported = defaultServiceSupported();
+ }
+ if ((mBuilderFieldsSet & 0x200) == 0) {
+ mClientSupported = defaultClientSupported();
+ }
InlineSuggestionsRequest o = new InlineSuggestionsRequest(
mMaxSuggestionCount,
mInlinePresentationSpecs,
@@ -673,12 +763,14 @@
mExtras,
mHostInputToken,
mHostDisplayId,
- mInlineTooltipPresentationSpec);
+ mInlineTooltipPresentationSpec,
+ mServiceSupported,
+ mClientSupported);
return o;
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x100) != 0) {
+ if ((mBuilderFieldsSet & 0x400) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -686,10 +778,10 @@
}
@DataClass.Generated(
- time = 1621415989607L,
- codegenVersion = "1.0.23",
+ time = 1615798784918L,
+ codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java",
- inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\nprivate @android.annotation.Nullable android.widget.inline.InlinePresentationSpec mInlineTooltipPresentationSpec\nprivate static final @android.compat.annotation.ChangeId @android.compat.annotation.EnabledSince long IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY\npublic void setHostInputToken(android.os.IBinder)\nprivate boolean extrasEquals(android.os.Bundle)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\npublic void filterContentTypes()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.widget.inline.InlinePresentationSpec defaultInlineTooltipPresentationSpec()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
+ inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\nprivate @android.annotation.Nullable android.widget.inline.InlinePresentationSpec mInlineTooltipPresentationSpec\nprivate boolean mServiceSupported\nprivate boolean mClientSupported\nprivate static final @android.compat.annotation.ChangeId @android.compat.annotation.EnabledSince long IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY\npublic void setHostInputToken(android.os.IBinder)\nprivate boolean extrasEquals(android.os.Bundle)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\npublic void filterContentTypes()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.widget.inline.InlinePresentationSpec defaultInlineTooltipPresentationSpec()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nprivate static boolean defaultServiceSupported()\nprivate static boolean defaultClientSupported()\npublic boolean isServiceSupported()\npublic boolean isClientSupported()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/webkit/WebViewLibraryLoader.java b/core/java/android/webkit/WebViewLibraryLoader.java
index be49fc4..91412d7 100644
--- a/core/java/android/webkit/WebViewLibraryLoader.java
+++ b/core/java/android/webkit/WebViewLibraryLoader.java
@@ -178,12 +178,23 @@
*/
static void reserveAddressSpaceInZygote() {
System.loadLibrary("webviewchromium_loader");
- boolean is64Bit = VMRuntime.getRuntime().is64Bit();
- // On 64-bit address space is really cheap and we can reserve 1GB which is plenty.
- // On 32-bit it's fairly scarce and we should keep it to a realistic number that
- // permits some future growth but doesn't hog space: we use 130MB which is roughly
- // what was calculated on older OS versions in practice.
- long addressSpaceToReserve = is64Bit ? 1 * 1024 * 1024 * 1024 : 130 * 1024 * 1024;
+ long addressSpaceToReserve;
+ if (VMRuntime.getRuntime().is64Bit()) {
+ // On 64-bit address space is really cheap and we can reserve 1GB which is plenty.
+ addressSpaceToReserve = 1 * 1024 * 1024 * 1024;
+ } else if (VMRuntime.getRuntime().vmInstructionSet().equals("arm")) {
+ // On 32-bit the address space is fairly scarce, hence we should keep it to a realistic
+ // number that permits some future growth but doesn't hog space. For ARM we use 130MB
+ // which is roughly what was calculated on older OS versions. The size has been
+ // growing gradually, but a few efforts have offset it back to the size close to the
+ // original.
+ addressSpaceToReserve = 130 * 1024 * 1024;
+ } else {
+ // The number below was obtained for a binary used for x86 emulators, allowing some
+ // natural growth.
+ addressSpaceToReserve = 190 * 1024 * 1024;
+ }
+
sAddressSpaceReserved = nativeReserveAddressSpace(addressSpaceToReserve);
if (sAddressSpaceReserved) {
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index f9f823b..26579c5d 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -480,6 +480,17 @@
return false;
}
+ /**
+ * @see View#onApplyWindowInsets(WindowInsets).
+ *
+ * <p>This is the entry point for the WebView implementation to override. It returns
+ * {@code null} when the WebView implementation hasn't implemented the WindowInsets support
+ * on S yet. In this case, the {@link View#onApplyWindowInsets()} super method will be
+ * called instead.
+ *
+ * @param insets Insets to apply
+ * @return The supplied insets with any applied insets consumed.
+ */
@SuppressWarnings("unused")
@Nullable
default WindowInsets onApplyWindowInsets(@Nullable WindowInsets insets) {
diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java
index d8f7f4c..f3dfda5 100644
--- a/core/java/android/widget/AdapterViewAnimator.java
+++ b/core/java/android/widget/AdapterViewAnimator.java
@@ -412,24 +412,29 @@
}
void refreshChildren() {
- if (mAdapter == null) return;
+ final int adapterCount = mAdapter == null ? 0 : getCount();
for (int i = mCurrentWindowStart; i <= mCurrentWindowEnd; i++) {
int index = modulo(i, getWindowSize());
- int adapterCount = getCount();
- // get the fresh child from the adapter
- final View updatedChild = mAdapter.getView(modulo(i, adapterCount), null, this);
+ final View updatedChild;
+ if (i < adapterCount) {
+ // get the fresh child from the adapter
+ updatedChild = mAdapter.getView(modulo(i, adapterCount), null, this);
- if (updatedChild.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
- updatedChild.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+ if (updatedChild.getImportantForAccessibility()
+ == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+ updatedChild.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+ }
+ } else {
+ updatedChild = null;
}
if (mViewsMap.containsKey(index)) {
final FrameLayout fl = (FrameLayout) mViewsMap.get(index).view;
- // add the new child to the frame, if it exists
+ // flush out the old child
+ fl.removeAllViewsInLayout();
if (updatedChild != null) {
- // flush out the old child
- fl.removeAllViewsInLayout();
+ // add the new child to the frame, if it exists
fl.addView(updatedChild);
}
}
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 8aa557b..f292c61 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -136,6 +136,7 @@
private int[] mState = null;
private boolean mMergeState = false;
+ private boolean mHasLevelSet = false;
private int mLevel = 0;
@UnsupportedAppUsage
private int mDrawableWidth;
@@ -798,6 +799,7 @@
@android.view.RemotableViewMethod
public void setImageLevel(int level) {
mLevel = level;
+ mHasLevelSet = true;
if (mDrawable != null) {
mDrawable.setLevel(level);
resizeFromDrawable();
@@ -1069,7 +1071,9 @@
: isAttachedToWindow() && getWindowVisibility() == VISIBLE && isShown();
d.setVisible(visible, true);
}
- d.setLevel(mLevel);
+ if (mHasLevelSet) {
+ d.setLevel(mLevel);
+ }
mDrawableWidth = d.getIntrinsicWidth();
mDrawableHeight = d.getIntrinsicHeight();
applyImageTint();
diff --git a/core/java/android/widget/OWNERS b/core/java/android/widget/OWNERS
index 64570a8..e1d6012 100644
--- a/core/java/android/widget/OWNERS
+++ b/core/java/android/widget/OWNERS
@@ -8,6 +8,6 @@
mount@google.com
njawad@google.com
-per-file TextView.java, EditText.java, Editor.java = siyamed@google.com, nona@google.com, clarabayarri@google.com
+per-file TextView*, EditText.java, Editor.java = siyamed@google.com, nona@google.com, clarabayarri@google.com
per-file SpellChecker.java = file:../view/inputmethod/OWNERS
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 1b76ebf..356d059 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -245,8 +245,6 @@
private boolean mAggregatedIsVisible;
- private CharSequence mCustomStateDescription = null;
-
private final ArrayList<RefreshData> mRefreshData = new ArrayList<RefreshData>();
private ObjectAnimator mLastProgressAnimator;
@@ -685,6 +683,9 @@
swapCurrentDrawable(mProgressDrawable);
stopAnimation();
}
+
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
}
@@ -1622,18 +1623,21 @@
@Override
@RemotableViewMethod
public void setStateDescription(@Nullable CharSequence stateDescription) {
- mCustomStateDescription = stateDescription;
- if (stateDescription == null) {
- super.setStateDescription(formatStateDescription(mProgress));
- } else {
- super.setStateDescription(stateDescription);
- }
+ // Assume the previous custom state description is different from default state description.
+ // Otherwise when the argument is null to restore the default state description, we will
+ // send out a state description changed event even though the state description presented to
+ // the user doesn't change. Since mStateDescription in View is private, we can't prevent
+ // this event from sending out.
+ super.setStateDescription(stateDescription);
}
void onProgressRefresh(float scale, boolean fromUser, int progress) {
if (AccessibilityManager.getInstance(mContext).isEnabled()
- && mCustomStateDescription == null) {
- super.setStateDescription(formatStateDescription(mProgress));
+ && getStateDescription() == null && !isIndeterminate()) {
+ AccessibilityEvent event = AccessibilityEvent.obtain();
+ event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+ event.setContentChangeTypes(AccessibilityEvent.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION);
+ sendAccessibilityEventUnchecked(event);
}
}
@@ -2356,7 +2360,15 @@
AccessibilityNodeInfo.RangeInfo.RANGE_TYPE_INT, getMin(), getMax(),
getProgress());
info.setRangeInfo(rangeInfo);
- info.setStateDescription(formatStateDescription(mProgress));
+ }
+
+ // Only set the default state description when custom state descripton is null.
+ if (getStateDescription() == null) {
+ if (isIndeterminate()) {
+ info.setStateDescription(getResources().getString(R.string.in_progress));
+ } else {
+ info.setStateDescription(formatStateDescription(mProgress));
+ }
}
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 3c4fd5e..116f3a3 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -202,6 +202,7 @@
import android.view.translation.ViewTranslationResponse;
import android.widget.RemoteViews.RemoteView;
+import com.android.internal.accessibility.util.AccessibilityUtils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -6320,6 +6321,11 @@
text = TextUtils.stringOrSpannedString(text);
}
+ @AccessibilityUtils.A11yTextChangeType int a11yTextChangeType = AccessibilityUtils.NONE;
+ if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ a11yTextChangeType = AccessibilityUtils.textOrSpanChanged(text, mText);
+ }
+
if (mAutoLinkMask != 0) {
Spannable s2;
@@ -6339,6 +6345,9 @@
* setText() again to try to upgrade the buffer type.
*/
setTextInternal(text);
+ if (a11yTextChangeType == AccessibilityUtils.NONE) {
+ a11yTextChangeType = AccessibilityUtils.PARCELABLE_SPAN;
+ }
// Do not change the movement method for text that support text selection as it
// would prevent an arbitrary cursor displacement.
@@ -6403,7 +6412,13 @@
sendOnTextChanged(text, 0, oldlen, textLength);
onTextChanged(text, 0, oldlen, textLength);
- notifyViewAccessibilityStateChangedIfNeeded(AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT);
+ if (a11yTextChangeType == AccessibilityUtils.TEXT) {
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT);
+ } else if (a11yTextChangeType == AccessibilityUtils.PARCELABLE_SPAN) {
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+ }
if (needEditableForNotification) {
sendAfterTextChanged((Editable) text);
diff --git a/core/java/android/window/DisplayAreaInfo.java b/core/java/android/window/DisplayAreaInfo.java
index 358467f..1a7aab6 100644
--- a/core/java/android/window/DisplayAreaInfo.java
+++ b/core/java/android/window/DisplayAreaInfo.java
@@ -16,6 +16,8 @@
package android.window;
+import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
+
import android.annotation.NonNull;
import android.annotation.TestApi;
import android.content.res.Configuration;
@@ -43,8 +45,17 @@
*/
public final int displayId;
+ /**
+ * The feature id of this display area.
+ */
public final int featureId;
+ /**
+ * The feature id of the root display area this display area is associated with.
+ * @hide
+ */
+ public int rootDisplayAreaId = FEATURE_UNDEFINED;
+
public DisplayAreaInfo(@NonNull WindowContainerToken token, int displayId, int featureId) {
this.token = token;
this.displayId = displayId;
@@ -56,6 +67,7 @@
configuration.readFromParcel(in);
displayId = in.readInt();
featureId = in.readInt();
+ rootDisplayAreaId = in.readInt();
}
@Override
@@ -64,6 +76,7 @@
configuration.writeToParcel(dest, flags);
dest.writeInt(displayId);
dest.writeInt(featureId);
+ dest.writeInt(rootDisplayAreaId);
}
@NonNull
diff --git a/core/java/android/window/DisplayAreaOrganizer.java b/core/java/android/window/DisplayAreaOrganizer.java
index 8784399..e674655 100644
--- a/core/java/android/window/DisplayAreaOrganizer.java
+++ b/core/java/android/window/DisplayAreaOrganizer.java
@@ -34,6 +34,15 @@
public class DisplayAreaOrganizer extends WindowOrganizer {
/**
+ * Key to specify the {@link com.android.server.wm.RootDisplayArea} to attach a window to.
+ * It will be used by the function passed in from
+ * {@link com.android.server.wm.DisplayAreaPolicyBuilder#setSelectRootForWindowFunc(BiFunction)}
+ * to find the Root DA to attach the window.
+ * @hide
+ */
+ public static final String KEY_ROOT_DISPLAY_AREA_ID = "root_display_area_id";
+
+ /**
* The value in display area indicating that no value has been set.
*/
public static final int FEATURE_UNDEFINED = -1;
diff --git a/core/java/android/window/IRemoteTransitionFinishedCallback.aidl b/core/java/android/window/IRemoteTransitionFinishedCallback.aidl
index 02aa1a9..7864c24 100644
--- a/core/java/android/window/IRemoteTransitionFinishedCallback.aidl
+++ b/core/java/android/window/IRemoteTransitionFinishedCallback.aidl
@@ -16,14 +16,18 @@
package android.window;
+import android.view.SurfaceControl;
import android.window.WindowContainerTransaction;
/**
* Interface to be invoked by the controlling process when a remote transition has finished.
*
* @see IRemoteTransition
+ * @param wct An optional WindowContainerTransaction to apply before the transition finished.
+ * @param sct An optional Surface Transaction that is added to the end of the finish/cleanup
+ * transaction. This is applied by shell.Transitions (before submitting the wct).
* {@hide}
*/
interface IRemoteTransitionFinishedCallback {
- void onTransitionFinished(in WindowContainerTransaction wct);
+ void onTransitionFinished(in WindowContainerTransaction wct, in SurfaceControl.Transaction sct);
}
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 23b8ee4..6430394 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -16,6 +16,12 @@
package android.window;
+import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
+import static android.app.ActivityOptions.ANIM_CUSTOM;
+import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
+import static android.app.ActivityOptions.ANIM_SCALE_UP;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
@@ -31,6 +37,7 @@
import android.app.ActivityManager;
import android.graphics.Point;
import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.Surface;
@@ -102,6 +109,8 @@
private SurfaceControl mRootLeash;
private final Point mRootOffset = new Point();
+ private AnimationOptions mOptions;
+
/** @hide */
public TransitionInfo(@WindowManager.TransitionOldType int type,
@WindowManager.TransitionFlags int flags) {
@@ -116,6 +125,7 @@
mRootLeash = new SurfaceControl();
mRootLeash.readFromParcel(in);
mRootOffset.readFromParcel(in);
+ mOptions = in.readTypedObject(AnimationOptions.CREATOR);
}
@Override
@@ -126,6 +136,7 @@
dest.writeList(mChanges);
mRootLeash.writeToParcel(dest, flags);
mRootOffset.writeToParcel(dest, flags);
+ dest.writeTypedObject(mOptions, flags);
}
@NonNull
@@ -154,6 +165,10 @@
mRootOffset.set(offsetLeft, offsetTop);
}
+ public void setAnimationOptions(AnimationOptions options) {
+ mOptions = options;
+ }
+
public int getType() {
return mType;
}
@@ -182,6 +197,10 @@
return mRootOffset;
}
+ public AnimationOptions getAnimationOptions() {
+ return mOptions;
+ }
+
@NonNull
public List<Change> getChanges() {
return mChanges;
@@ -484,4 +503,146 @@
+ mStartRotation + "->" + mEndRotation + "}";
}
}
+
+ /** Represents animation options during a transition */
+ public static final class AnimationOptions implements Parcelable {
+
+ private int mType;
+ private int mEnterResId;
+ private int mExitResId;
+ private boolean mOverrideTaskTransition;
+ private String mPackageName;
+ private final Rect mTransitionBounds = new Rect();
+ private HardwareBuffer mThumbnail;
+
+ private AnimationOptions(int type) {
+ mType = type;
+ }
+
+ public AnimationOptions(Parcel in) {
+ mType = in.readInt();
+ mEnterResId = in.readInt();
+ mExitResId = in.readInt();
+ mOverrideTaskTransition = in.readBoolean();
+ mPackageName = in.readString();
+ mTransitionBounds.readFromParcel(in);
+ mThumbnail = in.readTypedObject(HardwareBuffer.CREATOR);
+ }
+
+ public static AnimationOptions makeCustomAnimOptions(String packageName, int enterResId,
+ int exitResId, boolean overrideTaskTransition) {
+ AnimationOptions options = new AnimationOptions(ANIM_CUSTOM);
+ options.mPackageName = packageName;
+ options.mEnterResId = enterResId;
+ options.mExitResId = exitResId;
+ options.mOverrideTaskTransition = overrideTaskTransition;
+ return options;
+ }
+
+ public static AnimationOptions makeClipRevealAnimOptions(int startX, int startY, int width,
+ int height) {
+ AnimationOptions options = new AnimationOptions(ANIM_CLIP_REVEAL);
+ options.mTransitionBounds.set(startX, startY, startX + width, startY + height);
+ return options;
+ }
+
+ public static AnimationOptions makeScaleUpAnimOptions(int startX, int startY, int width,
+ int height) {
+ AnimationOptions options = new AnimationOptions(ANIM_SCALE_UP);
+ options.mTransitionBounds.set(startX, startY, startX + width, startY + height);
+ return options;
+ }
+
+ public static AnimationOptions makeThumnbnailAnimOptions(HardwareBuffer srcThumb,
+ int startX, int startY, boolean scaleUp) {
+ AnimationOptions options = new AnimationOptions(
+ scaleUp ? ANIM_THUMBNAIL_SCALE_UP : ANIM_THUMBNAIL_SCALE_DOWN);
+ options.mTransitionBounds.set(startX, startY, startX, startY);
+ options.mThumbnail = srcThumb;
+ return options;
+ }
+
+ public static AnimationOptions makeCrossProfileAnimOptions() {
+ AnimationOptions options = new AnimationOptions(ANIM_OPEN_CROSS_PROFILE_APPS);
+ return options;
+ }
+
+ public int getType() {
+ return mType;
+ }
+
+ public int getEnterResId() {
+ return mEnterResId;
+ }
+
+ public int getExitResId() {
+ return mExitResId;
+ }
+
+ public boolean getOverrideTaskTransition() {
+ return mOverrideTaskTransition;
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public Rect getTransitionBounds() {
+ return mTransitionBounds;
+ }
+
+ public HardwareBuffer getThumbnail() {
+ return mThumbnail;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mType);
+ dest.writeInt(mEnterResId);
+ dest.writeInt(mExitResId);
+ dest.writeBoolean(mOverrideTaskTransition);
+ dest.writeString(mPackageName);
+ mTransitionBounds.writeToParcel(dest, flags);
+ dest.writeTypedObject(mThumbnail, flags);
+ }
+
+ @NonNull
+ public static final Creator<AnimationOptions> CREATOR =
+ new Creator<AnimationOptions>() {
+ @Override
+ public AnimationOptions createFromParcel(Parcel in) {
+ return new AnimationOptions(in);
+ }
+
+ @Override
+ public AnimationOptions[] newArray(int size) {
+ return new AnimationOptions[size];
+ }
+ };
+
+ /** @hide */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull
+ private static String typeToString(int mode) {
+ switch(mode) {
+ case ANIM_CUSTOM: return "ANIM_CUSTOM";
+ case ANIM_CLIP_REVEAL: return "ANIM_CLIP_REVEAL";
+ case ANIM_SCALE_UP: return "ANIM_SCALE_UP";
+ case ANIM_THUMBNAIL_SCALE_UP: return "ANIM_THUMBNAIL_SCALE_UP";
+ case ANIM_THUMBNAIL_SCALE_DOWN: return "ANIM_THUMBNAIL_SCALE_DOWN";
+ case ANIM_OPEN_CROSS_PROFILE_APPS: return "ANIM_OPEN_CROSS_PROFILE_APPS";
+ default: return "<unknown:" + mode + ">";
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "{ AnimationOtions type= " + typeToString(mType) + " package=" + mPackageName
+ + " override=" + mOverrideTaskTransition + " b=" + mTransitionBounds + "}";
+ }
+ }
}
diff --git a/core/java/android/window/WindowContext.java b/core/java/android/window/WindowContext.java
index 901625b..69d7b4c 100644
--- a/core/java/android/window/WindowContext.java
+++ b/core/java/android/window/WindowContext.java
@@ -57,8 +57,14 @@
*
* @param base Base {@link Context} for this new instance.
* @param type Window type to be used with this context.
- * @param options A bundle used to pass window-related options.
- *
+ * @param options A bundle used to pass window-related options. For example, on device with
+ * multiple DisplayAreaGroups, one may specify the RootDisplayArea for the window
+ * using {@link DisplayAreaOrganizer#KEY_ROOT_DISPLAY_AREA_ID} in the options.
+ * Example usage:
+ * Bundle options = new Bundle();
+ * options.put(KEY_ROOT_DISPLAY_AREA_ID, displayAreaInfo.rootDisplayAreaId);
+ * Context windowContext = context.createWindowContext(display, type, options);
+ * @see DisplayAreaInfo#rootDisplayAreaId
* @hide
*/
public WindowContext(@NonNull Context base, int type, @Nullable Bundle options) {
diff --git a/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java b/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java
index 4b4e20f9..6a976ef 100644
--- a/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java
+++ b/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java
@@ -20,16 +20,23 @@
import static com.android.internal.accessibility.common.ShortcutConstants.SERVICES_SEPARATOR;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
import android.os.Build;
import android.os.UserHandle;
import android.provider.Settings;
+import android.text.ParcelableSpan;
+import android.text.Spanned;
import android.text.TextUtils;
import android.util.ArraySet;
import android.view.accessibility.AccessibilityManager;
+import libcore.util.EmptyArray;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -39,7 +46,25 @@
* Collection of utilities for accessibility service.
*/
public final class AccessibilityUtils {
- private AccessibilityUtils() {}
+ private AccessibilityUtils() {
+ }
+
+ /** @hide */
+ @IntDef(value = {
+ NONE,
+ TEXT,
+ PARCELABLE_SPAN
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface A11yTextChangeType {
+ }
+
+ /** Specifies no content has been changed for accessibility. */
+ public static final int NONE = 0;
+ /** Specifies some readable sequence has been changed. */
+ public static final int TEXT = 1;
+ /** Specifies some parcelable spans has been changed. */
+ public static final int PARCELABLE_SPAN = 2;
/**
* Returns the set of enabled accessibility services for userId. If there are no
@@ -168,4 +193,55 @@
Settings.Secure.USER_SETUP_COMPLETE, /* def= */ 0, UserHandle.USER_CURRENT)
!= /* false */ 0;
}
+
+ /**
+ * Returns the text change type for accessibility. It only cares about readable sequence changes
+ * or {@link ParcelableSpan} changes which are able to pass via IPC.
+ *
+ * @param before The CharSequence before changing
+ * @param after The CharSequence after changing
+ * @return Returns {@code TEXT} for readable sequence changes or {@code PARCELABLE_SPAN} for
+ * ParcelableSpan changes. Otherwise, returns {@code NONE}.
+ */
+ @A11yTextChangeType
+ public static int textOrSpanChanged(CharSequence before, CharSequence after) {
+ if (!TextUtils.equals(before, after)) {
+ return TEXT;
+ }
+ if (before instanceof Spanned || after instanceof Spanned) {
+ if (!parcelableSpansEquals(before, after)) {
+ return PARCELABLE_SPAN;
+ }
+ }
+ return NONE;
+ }
+
+ private static boolean parcelableSpansEquals(CharSequence before, CharSequence after) {
+ Object[] spansA = EmptyArray.OBJECT;
+ Object[] spansB = EmptyArray.OBJECT;
+ Spanned a = null;
+ Spanned b = null;
+ if (before instanceof Spanned) {
+ a = (Spanned) before;
+ spansA = a.getSpans(0, a.length(), ParcelableSpan.class);
+ }
+ if (after instanceof Spanned) {
+ b = (Spanned) after;
+ spansB = b.getSpans(0, b.length(), ParcelableSpan.class);
+ }
+ if (spansA.length != spansB.length) {
+ return false;
+ }
+ for (int i = 0; i < spansA.length; ++i) {
+ final Object thisSpan = spansA[i];
+ final Object otherSpan = spansB[i];
+ if ((thisSpan.getClass() != otherSpan.getClass())
+ || (a.getSpanStart(thisSpan) != b.getSpanStart(otherSpan))
+ || (a.getSpanEnd(thisSpan) != b.getSpanEnd(otherSpan))
+ || (a.getSpanFlags(thisSpan) != b.getSpanFlags(otherSpan))) {
+ return false;
+ }
+ }
+ return true;
+ }
}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 08db74f..37f6e49 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -2120,10 +2120,10 @@
+ " resultList.size()=" + resultList.size()
+ " appTargets.size()=" + appTargets.size());
}
-
+ Context selectedProfileContext = createContextAsUser(userHandle, 0 /* flags */);
for (int i = resultList.size() - 1; i >= 0; i--) {
final String packageName = resultList.get(i).getTargetComponent().getPackageName();
- if (!isPackageEnabled(packageName)) {
+ if (!isPackageEnabled(selectedProfileContext, packageName)) {
resultList.remove(i);
if (appTargets != null) {
appTargets.remove(i);
@@ -2175,13 +2175,13 @@
mChooserHandler.sendMessage(msg);
}
- private boolean isPackageEnabled(String packageName) {
+ private boolean isPackageEnabled(Context context, String packageName) {
if (TextUtils.isEmpty(packageName)) {
return false;
}
ApplicationInfo appInfo;
try {
- appInfo = getPackageManager().getApplicationInfo(packageName, 0);
+ appInfo = context.getPackageManager().getApplicationInfo(packageName, 0);
} catch (NameNotFoundException e) {
return false;
}
diff --git a/core/java/com/android/internal/content/om/OWNERS b/core/java/com/android/internal/content/om/OWNERS
new file mode 100644
index 0000000..44fd9fd
--- /dev/null
+++ b/core/java/com/android/internal/content/om/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 568631
+include /core/java/android/content/om/OWNERS
diff --git a/core/java/com/android/internal/content/om/OverlayConfig.java b/core/java/com/android/internal/content/om/OverlayConfig.java
index b38f623e..3f3c9bd 100644
--- a/core/java/com/android/internal/content/om/OverlayConfig.java
+++ b/core/java/com/android/internal/content/om/OverlayConfig.java
@@ -111,7 +111,7 @@
// Rebase the system partitions and settings file on the specified root directory.
partitions = new ArrayList<>(PackagePartitions.getOrderedPartitions(
p -> new OverlayPartition(
- new File(rootDirectory, p.getFolder().getPath()),
+ new File(rootDirectory, p.getNonConicalFolder().getPath()),
p)));
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 5dfc5fa..d92b65bf 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -341,6 +341,19 @@
}
}
+ /**
+ * Listener for the battery stats reset.
+ */
+ public interface BatteryResetListener {
+
+ /**
+ * Callback invoked immediately prior to resetting battery stats.
+ */
+ void prepareForBatteryStatsReset();
+ }
+
+ private BatteryResetListener mBatteryResetListener;
+
public interface BatteryCallback {
public void batteryNeedsCpuUpdate();
public void batteryPowerChanged(boolean onBattery);
@@ -5078,46 +5091,48 @@
}
public void notePowerSaveModeLocked(boolean enabled) {
- notePowerSaveModeLocked(enabled, mClocks.elapsedRealtime(), mClocks.uptimeMillis(), false);
+ notePowerSaveModeLocked(enabled, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
}
/**
- * Handles power save mode state changes.
+ * Toggles the power save mode state.
*/
- public void notePowerSaveModeLocked(boolean enabled, long elapsedRealtimeMs, long uptimeMs,
- boolean forceLog) {
+ public void notePowerSaveModeLockedInit(boolean enabled, long elapsedRealtimeMs,
+ long uptimeMs) {
+ if (mPowerSaveModeEnabled != enabled) {
+ notePowerSaveModeLocked(enabled, elapsedRealtimeMs, uptimeMs);
+ } else {
+ // Log an initial value for BATTERY_SAVER_MODE_STATE_CHANGED in order to
+ // allow the atom to read all future state changes.
+ FrameworkStatsLog.write(FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED,
+ enabled
+ ? FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__ON
+ : FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__OFF);
+ }
+ }
+
+ public void notePowerSaveModeLocked(boolean enabled, long elapsedRealtimeMs, long uptimeMs) {
if (mPowerSaveModeEnabled != enabled) {
int stepState = enabled ? STEP_LEVEL_MODE_POWER_SAVE : 0;
- mModStepMode |= (mCurStepMode & STEP_LEVEL_MODE_POWER_SAVE) ^ stepState;
- mCurStepMode = (mCurStepMode & ~STEP_LEVEL_MODE_POWER_SAVE) | stepState;
+ mModStepMode |= (mCurStepMode&STEP_LEVEL_MODE_POWER_SAVE) ^ stepState;
+ mCurStepMode = (mCurStepMode&~STEP_LEVEL_MODE_POWER_SAVE) | stepState;
mPowerSaveModeEnabled = enabled;
if (enabled) {
mHistoryCur.states2 |= HistoryItem.STATE2_POWER_SAVE_FLAG;
- if (DEBUG_HISTORY) {
- Slog.v(TAG, "Power save mode enabled to: "
- + Integer.toHexString(mHistoryCur.states2));
- }
+ if (DEBUG_HISTORY) Slog.v(TAG, "Power save mode enabled to: "
+ + Integer.toHexString(mHistoryCur.states2));
mPowerSaveModeEnabledTimer.startRunningLocked(elapsedRealtimeMs);
} else {
mHistoryCur.states2 &= ~HistoryItem.STATE2_POWER_SAVE_FLAG;
- if (DEBUG_HISTORY) {
- Slog.v(TAG, "Power save mode disabled to: "
- + Integer.toHexString(mHistoryCur.states2));
- }
+ if (DEBUG_HISTORY) Slog.v(TAG, "Power save mode disabled to: "
+ + Integer.toHexString(mHistoryCur.states2));
mPowerSaveModeEnabledTimer.stopRunningLocked(elapsedRealtimeMs);
}
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
FrameworkStatsLog.write(FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED,
enabled
- ? FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__ON
- : FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__OFF);
- } else if (forceLog) {
- // Log an initial value for BATTERY_SAVER_MODE_STATE_CHANGED in order to
- // allow the atom to read all future state changes.
- FrameworkStatsLog.write(FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED,
- enabled
- ? FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__ON
- : FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__OFF);
+ ? FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__ON
+ : FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__OFF);
}
}
@@ -10736,6 +10751,10 @@
}
}
+ PowerProfile getPowerProfile() {
+ return mPowerProfile;
+ }
+
/**
* Starts tracking CPU time-in-state for threads of the system server process,
* keeping a separate account of threads receiving incoming binder calls.
@@ -11184,6 +11203,10 @@
mDischargeCounter.reset(false, elapsedRealtimeUs);
}
+ public void setBatteryResetListener(BatteryResetListener batteryResetListener) {
+ mBatteryResetListener = batteryResetListener;
+ }
+
public void resetAllStatsCmdLocked() {
final long mSecUptime = mClocks.uptimeMillis();
long uptimeUs = mSecUptime * 1000;
@@ -11219,6 +11242,10 @@
}
private void resetAllStatsLocked(long uptimeMillis, long elapsedRealtimeMillis) {
+ if (mBatteryResetListener != null) {
+ mBatteryResetListener.prepareForBatteryStatsReset();
+ }
+
final long uptimeUs = uptimeMillis * 1000;
final long elapsedRealtimeUs = elapsedRealtimeMillis * 1000;
mStartCount = 0;
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
index 3aaccdd..8943db6 100644
--- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
+++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
@@ -38,14 +38,24 @@
public class BatteryUsageStatsProvider {
private final Context mContext;
private final BatteryStats mStats;
+ private final BatteryUsageStatsStore mBatteryUsageStatsStore;
private final PowerProfile mPowerProfile;
private final Object mLock = new Object();
private List<PowerCalculator> mPowerCalculators;
public BatteryUsageStatsProvider(Context context, BatteryStats stats) {
+ this(context, stats, null);
+ }
+
+ @VisibleForTesting
+ public BatteryUsageStatsProvider(Context context, BatteryStats stats,
+ BatteryUsageStatsStore batteryUsageStatsStore) {
mContext = context;
mStats = stats;
- mPowerProfile = new PowerProfile(mContext);
+ mBatteryUsageStatsStore = batteryUsageStatsStore;
+ mPowerProfile = stats instanceof BatteryStatsImpl
+ ? ((BatteryStatsImpl) stats).getPowerProfile()
+ : new PowerProfile(context);
}
private List<PowerCalculator> getPowerCalculators() {
@@ -126,6 +136,15 @@
private BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query,
long currentTimeMs) {
+ if (query.getToTimestamp() == 0) {
+ return getCurrentBatteryUsageStats(query, currentTimeMs);
+ } else {
+ return getAggregatedBatteryUsageStats(query);
+ }
+ }
+
+ private BatteryUsageStats getCurrentBatteryUsageStats(BatteryUsageStatsQuery query,
+ long currentTimeMs) {
final long realtimeUs = elapsedRealtime() * 1000;
final long uptimeUs = uptimeMillis() * 1000;
@@ -209,6 +228,25 @@
BatteryStats.STATS_SINCE_CHARGED) / 1000;
}
+ private BatteryUsageStats getAggregatedBatteryUsageStats(BatteryUsageStatsQuery query) {
+ final boolean includePowerModels = (query.getFlags()
+ & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0;
+
+ final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
+ mStats.getCustomEnergyConsumerNames(), includePowerModels);
+ final long[] timestamps = mBatteryUsageStatsStore.listBatteryUsageStatsTimestamps();
+ for (long timestamp : timestamps) {
+ if (timestamp > query.getFromTimestamp() && timestamp <= query.getToTimestamp()) {
+ final BatteryUsageStats snapshot =
+ mBatteryUsageStatsStore.loadBatteryUsageStats(timestamp);
+ if (snapshot != null) {
+ builder.add(snapshot);
+ }
+ }
+ }
+ return builder.build();
+ }
+
private long elapsedRealtime() {
if (mStats instanceof BatteryStatsImpl) {
return ((BatteryStatsImpl) mStats).mClocks.elapsedRealtime();
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsStore.java b/core/java/com/android/internal/os/BatteryUsageStatsStore.java
new file mode 100644
index 0000000..5c97602
--- /dev/null
+++ b/core/java/com/android/internal/os/BatteryUsageStatsStore.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.BatteryUsageStats;
+import android.os.BatteryUsageStatsQuery;
+import android.os.Handler;
+import android.util.AtomicFile;
+import android.util.LongArray;
+import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.StandardOpenOption;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+import java.util.TreeMap;
+
+/**
+ * A storage mechanism for BatteryUsageStats snapshots.
+ */
+public class BatteryUsageStatsStore {
+ private static final String TAG = "BatteryUsageStatsStore";
+
+ private static final List<BatteryUsageStatsQuery> BATTERY_USAGE_STATS_QUERY = List.of(
+ new BatteryUsageStatsQuery.Builder()
+ .setMaxStatsAgeMs(0)
+ .includePowerModels()
+ .build());
+ private static final String BATTERY_USAGE_STATS_DIR = "battery-usage-stats";
+ private static final String SNAPSHOT_FILE_EXTENSION = ".bus";
+ private static final String DIR_LOCK_FILENAME = ".lock";
+ private static final String CONFIG_FILENAME = "config";
+ private static final String BATTERY_USAGE_STATS_BEFORE_RESET_TIMESTAMP_PROPERTY =
+ "BATTERY_USAGE_STATS_BEFORE_RESET_TIMESTAMP";
+ private static final long MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES = 100 * 1024;
+
+ private final Context mContext;
+ private final BatteryStatsImpl mBatteryStats;
+ private final File mStoreDir;
+ private final File mLockFile;
+ private final AtomicFile mConfigFile;
+ private final long mMaxStorageBytes;
+ private final Handler mHandler;
+ private final BatteryUsageStatsProvider mBatteryUsageStatsProvider;
+
+ public BatteryUsageStatsStore(Context context, BatteryStatsImpl stats, File systemDir,
+ Handler handler) {
+ this(context, stats, systemDir, handler, MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES);
+ }
+
+ @VisibleForTesting
+ public BatteryUsageStatsStore(Context context, BatteryStatsImpl batteryStats, File systemDir,
+ Handler handler, long maxStorageBytes) {
+ mContext = context;
+ mBatteryStats = batteryStats;
+ mStoreDir = new File(systemDir, BATTERY_USAGE_STATS_DIR);
+ mLockFile = new File(mStoreDir, DIR_LOCK_FILENAME);
+ mConfigFile = new AtomicFile(new File(mStoreDir, CONFIG_FILENAME));
+ mHandler = handler;
+ mMaxStorageBytes = maxStorageBytes;
+ mBatteryStats.setBatteryResetListener(this::prepareForBatteryStatsReset);
+ mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(mContext, mBatteryStats);
+ }
+
+ private void prepareForBatteryStatsReset() {
+ final List<BatteryUsageStats> stats =
+ mBatteryUsageStatsProvider.getBatteryUsageStats(BATTERY_USAGE_STATS_QUERY);
+ if (stats.isEmpty()) {
+ Slog.wtf(TAG, "No battery usage stats generated");
+ return;
+ }
+
+ mHandler.post(() -> storeBatteryUsageStats(stats.get(0)));
+ }
+
+ private void storeBatteryUsageStats(BatteryUsageStats stats) {
+ try (FileLock lock = lockSnapshotDirectory()) {
+ if (!mStoreDir.exists()) {
+ if (!mStoreDir.mkdirs()) {
+ Slog.e(TAG,
+ "Could not create a directory for battery usage stats snapshots");
+ return;
+ }
+ }
+ File file = makeSnapshotFilename(stats.getStatsEndTimestamp());
+ try {
+ writeXmlFileLocked(stats, file);
+ } catch (Exception e) {
+ Slog.e(TAG, "Cannot save battery usage stats", e);
+ }
+
+ removeOldSnapshotsLocked();
+ } catch (IOException e) {
+ Slog.e(TAG, "Cannot lock battery usage stats directory", e);
+ }
+ }
+
+ /**
+ * Returns the timestamps of the stored BatteryUsageStats snapshots. The timestamp corresponds
+ * to the time the snapshot was taken {@link BatteryUsageStats#getStatsEndTimestamp()}.
+ */
+ public long[] listBatteryUsageStatsTimestamps() {
+ LongArray timestamps = new LongArray(100);
+ try (FileLock lock = lockSnapshotDirectory()) {
+ for (File file : mStoreDir.listFiles()) {
+ String fileName = file.getName();
+ if (fileName.endsWith(SNAPSHOT_FILE_EXTENSION)) {
+ try {
+ String fileNameWithoutExtension = fileName.substring(0,
+ fileName.length() - SNAPSHOT_FILE_EXTENSION.length());
+ timestamps.add(Long.parseLong(fileNameWithoutExtension));
+ } catch (NumberFormatException e) {
+ Slog.wtf(TAG, "Invalid format of BatteryUsageStats snapshot file name: "
+ + fileName);
+ }
+ }
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Cannot lock battery usage stats directory", e);
+ }
+ return timestamps.toArray();
+ }
+
+ /**
+ * Reads the specified snapshot of BatteryUsageStats. Returns null if the snapshot
+ * does not exist.
+ */
+ @Nullable
+ public BatteryUsageStats loadBatteryUsageStats(long timestamp) {
+ try (FileLock lock = lockSnapshotDirectory()) {
+ File file = makeSnapshotFilename(timestamp);
+ try {
+ return readXmlFileLocked(file);
+ } catch (Exception e) {
+ Slog.e(TAG, "Cannot read battery usage stats", e);
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Cannot lock battery usage stats directory", e);
+ }
+ return null;
+ }
+
+ /**
+ * Saves the supplied timestamp of the BATTERY_USAGE_STATS_BEFORE_RESET statsd atom pull
+ * in persistent file.
+ */
+ public void setLastBatteryUsageStatsBeforeResetAtomPullTimestamp(long timestamp) {
+ Properties props = new Properties();
+ try (FileLock lock = lockSnapshotDirectory()) {
+ try (InputStream in = mConfigFile.openRead()) {
+ props.load(in);
+ } catch (IOException e) {
+ Slog.e(TAG, "Cannot load config file " + mConfigFile, e);
+ }
+ props.put(BATTERY_USAGE_STATS_BEFORE_RESET_TIMESTAMP_PROPERTY,
+ String.valueOf(timestamp));
+ FileOutputStream out = null;
+ try {
+ out = mConfigFile.startWrite();
+ props.store(out, "Statsd atom pull timestamps");
+ mConfigFile.finishWrite(out);
+ } catch (IOException e) {
+ mConfigFile.failWrite(out);
+ Slog.e(TAG, "Cannot save config file " + mConfigFile, e);
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Cannot lock battery usage stats directory", e);
+ }
+ }
+
+ /**
+ * Retrieves the previously saved timestamp of the last BATTERY_USAGE_STATS_BEFORE_RESET
+ * statsd atom pull.
+ */
+ public long getLastBatteryUsageStatsBeforeResetAtomPullTimestamp() {
+ Properties props = new Properties();
+ try (FileLock lock = lockSnapshotDirectory()) {
+ try (InputStream in = mConfigFile.openRead()) {
+ props.load(in);
+ } catch (IOException e) {
+ Slog.e(TAG, "Cannot load config file " + mConfigFile, e);
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Cannot lock battery usage stats directory", e);
+ }
+ return Long.parseLong(
+ props.getProperty(BATTERY_USAGE_STATS_BEFORE_RESET_TIMESTAMP_PROPERTY, "0"));
+ }
+
+ private FileLock lockSnapshotDirectory() throws IOException {
+ mLockFile.getParentFile().mkdirs();
+ mLockFile.createNewFile();
+ return FileChannel.open(mLockFile.toPath(), StandardOpenOption.WRITE).lock();
+ }
+
+ /**
+ * Creates a file name by formatting the timestamp as 19-digit zero-padded number.
+ * This ensures that sorted directory list follows the chronological order.
+ */
+ private File makeSnapshotFilename(long statsEndTimestamp) {
+ return new File(mStoreDir, String.format(Locale.ENGLISH, "%019d", statsEndTimestamp)
+ + SNAPSHOT_FILE_EXTENSION);
+ }
+
+ private void writeXmlFileLocked(BatteryUsageStats stats, File file) throws IOException {
+ try (OutputStream out = new FileOutputStream(file)) {
+ TypedXmlSerializer serializer = Xml.newBinarySerializer();
+ serializer.setOutput(out, StandardCharsets.UTF_8.name());
+ serializer.startDocument(null, true);
+ stats.writeXml(serializer);
+ serializer.endDocument();
+ }
+ }
+
+ private BatteryUsageStats readXmlFileLocked(File file)
+ throws IOException, XmlPullParserException {
+ try (InputStream in = new FileInputStream(file)) {
+ TypedXmlPullParser parser = Xml.newBinaryPullParser();
+ parser.setInput(in, StandardCharsets.UTF_8.name());
+ return BatteryUsageStats.createFromXml(parser);
+ }
+ }
+
+ private void removeOldSnapshotsLocked() {
+ // Read the directory list into a _sorted_ map. The alphanumeric ordering
+ // corresponds to the historical order of snapshots because the file names
+ // are timestamps zero-padded to the same length.
+ long totalSize = 0;
+ TreeMap<File, Long> mFileSizes = new TreeMap<>();
+ for (File file : mStoreDir.listFiles()) {
+ final long fileSize = file.length();
+ totalSize += fileSize;
+ if (file.getName().endsWith(SNAPSHOT_FILE_EXTENSION)) {
+ mFileSizes.put(file, fileSize);
+ }
+ }
+
+ while (totalSize > mMaxStorageBytes) {
+ final Map.Entry<File, Long> entry = mFileSizes.firstEntry();
+ if (entry == null) {
+ break;
+ }
+
+ File file = entry.getKey();
+ if (!file.delete()) {
+ Slog.e(TAG, "Cannot delete battery usage stats " + file);
+ }
+ totalSize -= entry.getValue();
+ mFileSizes.remove(file);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/os/WrapperInit.java b/core/java/com/android/internal/os/WrapperInit.java
index 6860759e..508782b 100644
--- a/core/java/com/android/internal/os/WrapperInit.java
+++ b/core/java/com/android/internal/os/WrapperInit.java
@@ -21,8 +21,8 @@
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
-import android.system.StructCapUserData;
-import android.system.StructCapUserHeader;
+import android.system.StructUserCapData;
+import android.system.StructUserCapHeader;
import android.util.Slog;
import android.util.TimingsTraceLog;
@@ -187,9 +187,9 @@
* capabilities, which may make it crash, but not exceed its allowances.
*/
private static void preserveCapabilities() {
- StructCapUserHeader header = new StructCapUserHeader(
+ StructUserCapHeader header = new StructUserCapHeader(
OsConstants._LINUX_CAPABILITY_VERSION_3, 0);
- StructCapUserData[] data;
+ StructUserCapData[] data;
try {
data = Os.capget(header);
} catch (ErrnoException e) {
@@ -199,9 +199,9 @@
if (data[0].permitted != data[0].inheritable ||
data[1].permitted != data[1].inheritable) {
- data[0] = new StructCapUserData(data[0].effective, data[0].permitted,
+ data[0] = new StructUserCapData(data[0].effective, data[0].permitted,
data[0].permitted);
- data[1] = new StructCapUserData(data[1].effective, data[1].permitted,
+ data[1] = new StructUserCapData(data[1].effective, data[1].permitted,
data[1].permitted);
try {
Os.capset(header, data);
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 6ff656c..ee35a84 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -45,8 +45,8 @@
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
-import android.system.StructCapUserData;
-import android.system.StructCapUserHeader;
+import android.system.StructUserCapData;
+import android.system.StructUserCapHeader;
import android.text.Hyphenator;
import android.util.EventLog;
import android.util.Log;
@@ -747,9 +747,9 @@
OsConstants.CAP_BLOCK_SUSPEND
);
/* Containers run without some capabilities, so drop any caps that are not available. */
- StructCapUserHeader header = new StructCapUserHeader(
+ StructUserCapHeader header = new StructUserCapHeader(
OsConstants._LINUX_CAPABILITY_VERSION_3, 0);
- StructCapUserData[] data;
+ StructUserCapData[] data;
try {
data = Os.capget(header);
} catch (ErrnoException ex) {
diff --git a/core/java/com/android/internal/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java
index 60a8d80..d3224b1 100644
--- a/core/java/com/android/internal/policy/TransitionAnimation.java
+++ b/core/java/com/android/internal/policy/TransitionAnimation.java
@@ -16,16 +16,20 @@
package com.android.internal.policy;
+import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_NONE;
import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_OPEN;
+import static android.view.WindowManager.TRANSIT_OPEN;
+import android.annotation.DrawableRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -34,12 +38,18 @@
import android.content.res.ResourceId;
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Picture;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.hardware.HardwareBuffer;
import android.os.SystemProperties;
import android.util.Slog;
import android.view.WindowManager.LayoutParams;
import android.view.WindowManager.TransitionOldType;
+import android.view.WindowManager.TransitionType;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
@@ -56,11 +66,17 @@
/** @hide */
public class TransitionAnimation {
+ public static final int WALLPAPER_TRANSITION_NONE = 0;
+ public static final int WALLPAPER_TRANSITION_OPEN = 1;
+ public static final int WALLPAPER_TRANSITION_CLOSE = 2;
+ public static final int WALLPAPER_TRANSITION_INTRA_OPEN = 3;
+ public static final int WALLPAPER_TRANSITION_INTRA_CLOSE = 4;
+
// These are the possible states for the enter/exit activities during a thumbnail transition
- public static final int THUMBNAIL_TRANSITION_ENTER_SCALE_UP = 0;
- public static final int THUMBNAIL_TRANSITION_EXIT_SCALE_UP = 1;
- public static final int THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN = 2;
- public static final int THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN = 3;
+ private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_UP = 0;
+ private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_UP = 1;
+ private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN = 2;
+ private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN = 3;
/**
* Maximum duration for the clip reveal animation. This is used when there is a lot of movement
@@ -72,9 +88,15 @@
public static final int DEFAULT_APP_TRANSITION_DURATION = 336;
+ /** Fraction of animation at which the recents thumbnail stays completely transparent */
+ private static final float RECENTS_THUMBNAIL_FADEIN_FRACTION = 0.5f;
/** Fraction of animation at which the recents thumbnail becomes completely transparent */
private static final float RECENTS_THUMBNAIL_FADEOUT_FRACTION = 0.5f;
+ /** Interpolator to be used for animations that respond directly to a touch */
+ static final Interpolator TOUCH_RESPONSE_INTERPOLATOR =
+ new PathInterpolator(0.3f, 0f, 0.1f, 1f);
+
private static final String DEFAULT_PACKAGE = "android";
private final Context mContext;
@@ -86,7 +108,9 @@
new PathInterpolator(0.3f, 0f, 0.1f, 1f);
private final Interpolator mClipHorizontalInterpolator = new PathInterpolator(0, 0, 0.4f, 1f);
private final Interpolator mDecelerateInterpolator;
+ private final Interpolator mFastOutLinearInInterpolator;
private final Interpolator mLinearOutSlowInInterpolator;
+ private final Interpolator mThumbnailFadeInInterpolator;
private final Interpolator mThumbnailFadeOutInterpolator;
private final Rect mTmpFromClipRect = new Rect();
private final Rect mTmpToClipRect = new Rect();
@@ -107,8 +131,19 @@
mDecelerateInterpolator = AnimationUtils.loadInterpolator(context,
com.android.internal.R.interpolator.decelerate_cubic);
+ mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.fast_out_linear_in);
mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
com.android.internal.R.interpolator.linear_out_slow_in);
+ mThumbnailFadeInInterpolator = input -> {
+ // Linear response for first fraction, then complete after that.
+ if (input < RECENTS_THUMBNAIL_FADEIN_FRACTION) {
+ return 0f;
+ }
+ float t = (input - RECENTS_THUMBNAIL_FADEIN_FRACTION)
+ / (1f - RECENTS_THUMBNAIL_FADEIN_FRACTION);
+ return mFastOutLinearInInterpolator.getInterpolation(t);
+ };
mThumbnailFadeOutInterpolator = input -> {
// Linear response for first fraction, then complete after that.
if (input < RECENTS_THUMBNAIL_FADEOUT_FRACTION) {
@@ -181,6 +216,13 @@
DEFAULT_PACKAGE, com.android.internal.R.anim.cross_profile_apps_thumbnail_enter);
}
+ @Nullable
+ public Animation createCrossProfileAppsThumbnailAnimationLocked(Rect appRect) {
+ final Animation animation = loadCrossProfileAppThumbnailEnterAnimation();
+ return prepareThumbnailAnimationWithDuration(animation, appRect.width(),
+ appRect.height(), 0, null);
+ }
+
/** Load animation by resource Id from specific package. */
@Nullable
public Animation loadAnimationRes(String packageName, int resId) {
@@ -347,8 +389,15 @@
}
}
- public Animation createClipRevealAnimationLocked(int transit, boolean enter, Rect appFrame,
- Rect displayFrame, Rect startRect) {
+ public Animation createClipRevealAnimationLocked(@TransitionType int transit,
+ int wallpaperTransit, boolean enter, Rect appFrame, Rect displayFrame, Rect startRect) {
+ return createClipRevealAnimationLockedCompat(
+ getTransitCompatType(transit, wallpaperTransit), enter, appFrame, displayFrame,
+ startRect);
+ }
+
+ public Animation createClipRevealAnimationLockedCompat(@TransitionOldType int transit,
+ boolean enter, Rect appFrame, Rect displayFrame, Rect startRect) {
final Animation anim;
if (enter) {
final int appWidth = appFrame.width();
@@ -458,8 +507,14 @@
return anim;
}
- public Animation createScaleUpAnimationLocked(int transit, boolean enter,
- Rect containingFrame, Rect startRect) {
+ public Animation createScaleUpAnimationLocked(@TransitionType int transit, int wallpaperTransit,
+ boolean enter, Rect containingFrame, Rect startRect) {
+ return createScaleUpAnimationLockedCompat(getTransitCompatType(transit, wallpaperTransit),
+ enter, containingFrame, startRect);
+ }
+
+ public Animation createScaleUpAnimationLockedCompat(@TransitionOldType int transit,
+ boolean enter, Rect containingFrame, Rect startRect) {
Animation a;
setupDefaultNextAppTransitionStartRect(startRect, mTmpRect);
final int appWidth = containingFrame.width();
@@ -514,12 +569,19 @@
return a;
}
+ public Animation createThumbnailEnterExitAnimationLocked(boolean enter, boolean scaleUp,
+ Rect containingFrame, @TransitionType int transit, int wallpaperTransit,
+ HardwareBuffer thumbnailHeader, Rect startRect) {
+ return createThumbnailEnterExitAnimationLockedCompat(enter, scaleUp, containingFrame,
+ getTransitCompatType(transit, wallpaperTransit), thumbnailHeader, startRect);
+ }
+
/**
* This animation is created when we are doing a thumbnail transition, for the activity that is
* leaving, and the activity that is entering.
*/
- public Animation createThumbnailEnterExitAnimationLocked(int thumbTransitState,
- Rect containingFrame, int transit, HardwareBuffer thumbnailHeader,
+ public Animation createThumbnailEnterExitAnimationLockedCompat(boolean enter, boolean scaleUp,
+ Rect containingFrame, @TransitionOldType int transit, HardwareBuffer thumbnailHeader,
Rect startRect) {
final int appWidth = containingFrame.width();
final int appHeight = containingFrame.height();
@@ -529,6 +591,7 @@
final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
final int thumbHeightI = thumbnailHeader != null ? thumbnailHeader.getHeight() : appHeight;
final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
+ final int thumbTransitState = getThumbnailTransitionState(enter, scaleUp);
switch (thumbTransitState) {
case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: {
@@ -587,8 +650,8 @@
* This alternate animation is created when we are doing a thumbnail transition, for the
* activity that is leaving, and the activity that is entering.
*/
- public Animation createAspectScaledThumbnailEnterExitAnimationLocked(int thumbTransitState,
- int orientation, int transit, Rect containingFrame, Rect contentInsets,
+ public Animation createAspectScaledThumbnailEnterExitAnimationLocked(boolean enter,
+ boolean scaleUp, int orientation, int transit, Rect containingFrame, Rect contentInsets,
@Nullable Rect surfaceInsets, @Nullable Rect stableInsets, boolean freeform,
Rect startRect, Rect defaultStartRect) {
Animation a;
@@ -601,11 +664,11 @@
final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
final int thumbStartX = mTmpRect.left - containingFrame.left - contentInsets.left;
final int thumbStartY = mTmpRect.top - containingFrame.top;
+ final int thumbTransitState = getThumbnailTransitionState(enter, scaleUp);
switch (thumbTransitState) {
case THUMBNAIL_TRANSITION_ENTER_SCALE_UP:
case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
- final boolean scaleUp = thumbTransitState == THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
if (freeform && scaleUp) {
a = createAspectScaledThumbnailEnterFreeformAnimationLocked(
containingFrame, surfaceInsets, startRect, defaultStartRect);
@@ -720,10 +783,151 @@
}
/**
+ * This animation runs for the thumbnail that gets cross faded with the enter/exit activity
+ * when a thumbnail is specified with the pending animation override.
+ */
+ public Animation createThumbnailAspectScaleAnimationLocked(Rect appRect,
+ @Nullable Rect contentInsets, HardwareBuffer thumbnailHeader, int orientation,
+ Rect startRect, Rect defaultStartRect, boolean scaleUp) {
+ Animation a;
+ final int thumbWidthI = thumbnailHeader.getWidth();
+ final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
+ final int thumbHeightI = thumbnailHeader.getHeight();
+ final int appWidth = appRect.width();
+
+ float scaleW = appWidth / thumbWidth;
+ getNextAppTransitionStartRect(startRect, defaultStartRect, mTmpRect);
+ final float fromX;
+ float fromY;
+ final float toX;
+ float toY;
+ final float pivotX;
+ final float pivotY;
+ if (shouldScaleDownThumbnailTransition(orientation)) {
+ fromX = mTmpRect.left;
+ fromY = mTmpRect.top;
+
+ // For the curved translate animation to work, the pivot points needs to be at the
+ // same absolute position as the one from the real surface.
+ toX = mTmpRect.width() / 2 * (scaleW - 1f) + appRect.left;
+ toY = appRect.height() / 2 * (1 - 1 / scaleW) + appRect.top;
+ pivotX = mTmpRect.width() / 2;
+ pivotY = appRect.height() / 2 / scaleW;
+ if (mGridLayoutRecentsEnabled) {
+ // In the grid layout, the header is displayed above the thumbnail instead of
+ // overlapping it.
+ fromY -= thumbHeightI;
+ toY -= thumbHeightI * scaleW;
+ }
+ } else {
+ pivotX = 0;
+ pivotY = 0;
+ fromX = mTmpRect.left;
+ fromY = mTmpRect.top;
+ toX = appRect.left;
+ toY = appRect.top;
+ }
+ if (scaleUp) {
+ // Animation up from the thumbnail to the full screen
+ Animation scale = new ScaleAnimation(1f, scaleW, 1f, scaleW, pivotX, pivotY);
+ scale.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
+ scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+ Animation alpha = new AlphaAnimation(1f, 0f);
+ alpha.setInterpolator(mThumbnailFadeOutInterpolator);
+ alpha.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+ Animation translate = createCurvedMotion(fromX, toX, fromY, toY);
+ translate.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
+ translate.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+
+ mTmpFromClipRect.set(0, 0, thumbWidthI, thumbHeightI);
+ mTmpToClipRect.set(appRect);
+
+ // Containing frame is in screen space, but we need the clip rect in the
+ // app space.
+ mTmpToClipRect.offsetTo(0, 0);
+ mTmpToClipRect.right = (int) (mTmpToClipRect.right / scaleW);
+ mTmpToClipRect.bottom = (int) (mTmpToClipRect.bottom / scaleW);
+
+ if (contentInsets != null) {
+ mTmpToClipRect.inset((int) (-contentInsets.left * scaleW),
+ (int) (-contentInsets.top * scaleW),
+ (int) (-contentInsets.right * scaleW),
+ (int) (-contentInsets.bottom * scaleW));
+ }
+
+ Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
+ clipAnim.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
+ clipAnim.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+
+ // This AnimationSet uses the Interpolators assigned above.
+ AnimationSet set = new AnimationSet(false);
+ set.addAnimation(scale);
+ if (!mGridLayoutRecentsEnabled) {
+ // In the grid layout, the header should be shown for the whole animation.
+ set.addAnimation(alpha);
+ }
+ set.addAnimation(translate);
+ set.addAnimation(clipAnim);
+ a = set;
+ } else {
+ // Animation down from the full screen to the thumbnail
+ Animation scale = new ScaleAnimation(scaleW, 1f, scaleW, 1f, pivotX, pivotY);
+ scale.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
+ scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+ Animation alpha = new AlphaAnimation(0f, 1f);
+ alpha.setInterpolator(mThumbnailFadeInInterpolator);
+ alpha.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+ Animation translate = createCurvedMotion(toX, fromX, toY, fromY);
+ translate.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
+ translate.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+
+ // This AnimationSet uses the Interpolators assigned above.
+ AnimationSet set = new AnimationSet(false);
+ set.addAnimation(scale);
+ if (!mGridLayoutRecentsEnabled) {
+ // In the grid layout, the header should be shown for the whole animation.
+ set.addAnimation(alpha);
+ }
+ set.addAnimation(translate);
+ a = set;
+
+ }
+ return prepareThumbnailAnimationWithDuration(a, appWidth, appRect.height(), 0,
+ null);
+ }
+
+ /**
+ * Creates an overlay with a background color and a thumbnail for the cross profile apps
+ * animation.
+ */
+ public HardwareBuffer createCrossProfileAppsThumbnail(
+ @DrawableRes int thumbnailDrawableRes, Rect frame) {
+ final int width = frame.width();
+ final int height = frame.height();
+
+ final Picture picture = new Picture();
+ final Canvas canvas = picture.beginRecording(width, height);
+ canvas.drawColor(Color.argb(0.6f, 0, 0, 0));
+ final int thumbnailSize = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.cross_profile_apps_thumbnail_size);
+ final Drawable drawable = mContext.getDrawable(thumbnailDrawableRes);
+ drawable.setBounds(
+ (width - thumbnailSize) / 2,
+ (height - thumbnailSize) / 2,
+ (width + thumbnailSize) / 2,
+ (height + thumbnailSize) / 2);
+ drawable.setTint(mContext.getColor(android.R.color.white));
+ drawable.draw(canvas);
+ picture.endRecording();
+
+ return Bitmap.createBitmap(picture).getHardwareBuffer();
+ }
+
+ /**
* Prepares the specified animation with a standard duration, interpolator, etc.
*/
private Animation prepareThumbnailAnimation(Animation a, int appWidth, int appHeight,
- int transit) {
+ @TransitionOldType int transit) {
// Pick the desired duration. If this is an inter-activity transition,
// it is the standard duration for that. Otherwise we use the longer
// task transition duration.
@@ -820,6 +1024,22 @@
return anim;
}
+ private static @TransitionOldType int getTransitCompatType(@TransitionType int transit,
+ int wallpaperTransit) {
+ if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_OPEN) {
+ return TRANSIT_OLD_WALLPAPER_INTRA_OPEN;
+ } else if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_CLOSE) {
+ return TRANSIT_OLD_WALLPAPER_INTRA_CLOSE;
+ } else if (transit == TRANSIT_OPEN) {
+ return TRANSIT_OLD_ACTIVITY_OPEN;
+ } else if (transit == TRANSIT_CLOSE) {
+ return TRANSIT_OLD_ACTIVITY_CLOSE;
+ }
+
+ // We only do some special handle for above type, so use type NONE for default behavior.
+ return TRANSIT_OLD_NONE;
+ }
+
/**
* Calculates the duration for the clip reveal animation. If the clip is "cut off", meaning that
* the start rect is outside of the target rect, and there is a lot of movement going on.
@@ -843,10 +1063,33 @@
}
/**
+ * Return the current thumbnail transition state.
+ */
+ private int getThumbnailTransitionState(boolean enter, boolean scaleUp) {
+ if (enter) {
+ if (scaleUp) {
+ return THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
+ } else {
+ return THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN;
+ }
+ } else {
+ if (scaleUp) {
+ return THUMBNAIL_TRANSITION_EXIT_SCALE_UP;
+ } else {
+ return THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN;
+ }
+ }
+ }
+
+ /**
* Prepares the specified animation with a standard duration, interpolator, etc.
*/
- private static Animation prepareThumbnailAnimationWithDuration(Animation a, int appWidth,
+ public static Animation prepareThumbnailAnimationWithDuration(Animation a, int appWidth,
int appHeight, long duration, Interpolator interpolator) {
+ if (a == null) {
+ return null;
+ }
+
if (duration > 0) {
a.setDuration(duration);
}
diff --git a/core/java/com/android/internal/util/OWNERS b/core/java/com/android/internal/util/OWNERS
index a045451..5b68159 100644
--- a/core/java/com/android/internal/util/OWNERS
+++ b/core/java/com/android/internal/util/OWNERS
@@ -1,5 +1,6 @@
per-file AsyncChannel* = lorenzo@google.com, satk@google.com, etancohen@google.com
per-file MessageUtils*, Protocol*, RingBuffer*, TokenBucket* = jchalard@google.com, lorenzo@google.com, satk@google.com
+per-file *Notification* = file:/services/core/java/com/android/server/notification/OWNERS
per-file Protocol* = etancohen@google.com, lorenzo@google.com
per-file State* = jchalard@google.com, lorenzo@google.com, satk@google.com
per-file DataClass* = eugenesusla@google.com
\ No newline at end of file
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index d528428..4894b5c 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -882,8 +882,9 @@
}
static jobject nativeGetPhysicalDisplayToken(JNIEnv* env, jclass clazz, jlong physicalDisplayId) {
- sp<IBinder> token =
- SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId(physicalDisplayId));
+ const auto id = DisplayId::fromValue<PhysicalDisplayId>(physicalDisplayId);
+ if (!id) return nullptr;
+ sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(*id);
return javaObjectForIBinder(env, token);
}
@@ -1006,6 +1007,17 @@
}
}
+static void nativeSetDisplayFlags(JNIEnv* env, jclass clazz, jlong transactionObj, jobject tokenObj,
+ jint flags) {
+ sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
+ if (token == NULL) return;
+
+ {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+ transaction->setDisplayFlags(token, flags);
+ }
+}
+
static void nativeSetDisplayProjection(JNIEnv* env, jclass clazz,
jlong transactionObj,
jobject tokenObj, jint orientation,
@@ -1863,6 +1875,8 @@
(void*)nativeSetDisplaySurface },
{"nativeSetDisplayLayerStack", "(JLandroid/os/IBinder;I)V",
(void*)nativeSetDisplayLayerStack },
+ {"nativeSetDisplayFlags", "(JLandroid/os/IBinder;I)V",
+ (void*)nativeSetDisplayFlags },
{"nativeSetDisplayProjection", "(JLandroid/os/IBinder;IIIIIIIII)V",
(void*)nativeSetDisplayProjection },
{"nativeSetDisplaySize", "(JLandroid/os/IBinder;II)V",
diff --git a/core/proto/android/server/accessibilitytrace.proto b/core/proto/android/server/accessibilitytrace.proto
index 1fc4a01..41fecfd 100644
--- a/core/proto/android/server/accessibilitytrace.proto
+++ b/core/proto/android/server/accessibilitytrace.proto
@@ -46,17 +46,17 @@
/* required: elapsed realtime in nanos since boot of when this entry was logged */
optional fixed64 elapsed_realtime_nanos = 1;
optional string calendar_time = 2;
-
- optional string process_name = 3;
- optional string thread_id_name = 4;
+ repeated string logging_type = 3;
+ optional string process_name = 4;
+ optional string thread_id_name = 5;
/* where the trace originated */
- optional string where = 5;
+ optional string where = 6;
- optional string calling_pkg = 6;
- optional string calling_params = 7;
- optional string calling_stacks = 8;
+ optional string calling_pkg = 7;
+ optional string calling_params = 8;
+ optional string calling_stacks = 9;
- optional AccessibilityDumpProto accessibility_service = 9;
- optional com.android.server.wm.WindowManagerServiceDumpProto window_manager_service = 10;
+ optional AccessibilityDumpProto accessibility_service = 10;
+ optional com.android.server.wm.WindowManagerServiceDumpProto window_manager_service = 11;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 64b8a1a..4350a82 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -202,6 +202,8 @@
<protected-broadcast
android:name="android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED" />
<protected-broadcast
+ android:name="android.bluetooth.volume-control.profile.action.CONNECTION_STATE_CHANGED" />
+ <protected-broadcast
android:name="android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast
android:name="android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED" />
@@ -2590,7 +2592,7 @@
third-party apps.
-->
<permission android:name="android.permission.MANAGE_DOCUMENTS"
- android:protectionLevel="signature|documenter" />
+ android:protectionLevel="signature|role" />
<!-- Allows an application to manage access to crates, usually as part
of a crates picker.
@@ -2607,7 +2609,7 @@
<p>Not for use by third-party applications.
-->
<permission android:name="android.permission.CACHE_CONTENT"
- android:protectionLevel="signature|documenter" />
+ android:protectionLevel="signature|role" />
<!-- @SystemApi @hide
Allows an application to aggressively allocate disk space.
@@ -2753,7 +2755,7 @@
<!-- @SystemApi @TestApi @hide Allows an application to change to remove/kill tasks -->
<permission android:name="android.permission.REMOVE_TASKS"
- android:protectionLevel="signature|documenter|recents" />
+ android:protectionLevel="signature|recents|role" />
<!-- @deprecated Use MANAGE_ACTIVITY_TASKS instead.
@SystemApi @TestApi @hide Allows an application to create/manage/remove stacks -->
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 85de02e..cfa0f5e 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"nie gemerk nie"</string>
<string name="selected" msgid="6614607926197755875">"gekies"</string>
<string name="not_selected" msgid="410652016565864475">"nie gekies nie"</string>
+ <string name="in_progress" msgid="2149208189184319441">"aan die gang"</string>
<string name="whichApplication" msgid="5432266899591255759">"Voltooi handeling met"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Voltooi handeling met gebruik van %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Voltooi handeling"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 18cc4c6..879c80b 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"ምልክት አልተደረገበትም"</string>
<string name="selected" msgid="6614607926197755875">"ተመርጧል"</string>
<string name="not_selected" msgid="410652016565864475">"አልተመረጠም"</string>
+ <string name="in_progress" msgid="2149208189184319441">"በሂደት ላይ"</string>
<string name="whichApplication" msgid="5432266899591255759">"... በመጠቀም ድርጊቱን አጠናቅ"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$sን ተጠቅመው እርምጃ ያጠናቅቁ"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"እርምጃውን አጠናቅቅ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 1d7ce15..c37c7a7 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1258,6 +1258,7 @@
<string name="not_checked" msgid="7972320087569023342">"لم يتم وضع علامة"</string>
<string name="selected" msgid="6614607926197755875">"محدّد"</string>
<string name="not_selected" msgid="410652016565864475">"غير محدّد"</string>
+ <string name="in_progress" msgid="2149208189184319441">"قيد التقدّم"</string>
<string name="whichApplication" msgid="5432266899591255759">"إكمال الإجراء باستخدام"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"إكمال الإجراء باستخدام %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"إكمال الإجراء"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index b1bf980..0fc6d4c 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"টিক চিহ্ন দিয়া হোৱা নাই"</string>
<string name="selected" msgid="6614607926197755875">"বাছনি কৰা"</string>
<string name="not_selected" msgid="410652016565864475">"বাছনি কৰা হোৱা নাই"</string>
+ <string name="in_progress" msgid="2149208189184319441">"চলি আছে"</string>
<string name="whichApplication" msgid="5432266899591255759">"এয়া ব্যৱহাৰ কৰি কার্য সম্পূর্ণ কৰক"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ব্যৱহাৰ কৰি কাৰ্যটো সম্পূৰ্ণ কৰক"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"কাৰ্য সম্পূৰ্ণ কৰক"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index af66b0b..6e6933a 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"yoxlanılmayıb"</string>
<string name="selected" msgid="6614607926197755875">"seçilib"</string>
<string name="not_selected" msgid="410652016565864475">"seçilməyib"</string>
+ <string name="in_progress" msgid="2149208189184319441">"davam edir"</string>
<string name="whichApplication" msgid="5432266899591255759">"Əməliyyatı tamamlayın:"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s istifadə edərək əməliyyatı tamamlayın"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Əməliyyatı tamamlayın"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 4cc46d9..762e3a9 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1198,6 +1198,7 @@
<string name="not_checked" msgid="7972320087569023342">"nije označeno"</string>
<string name="selected" msgid="6614607926197755875">"izabrano"</string>
<string name="not_selected" msgid="410652016565864475">"nije izabrano"</string>
+ <string name="in_progress" msgid="2149208189184319441">"u toku"</string>
<string name="whichApplication" msgid="5432266899591255759">"Dovrši radnju preko"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Završite radnju pomoću aplikacije %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Završi radnju"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 1423737..801c220 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1218,6 +1218,7 @@
<string name="not_checked" msgid="7972320087569023342">"не пазначана"</string>
<string name="selected" msgid="6614607926197755875">"выбраны"</string>
<string name="not_selected" msgid="410652016565864475">"не выбраны"</string>
+ <string name="in_progress" msgid="2149208189184319441">"выконваецца"</string>
<string name="whichApplication" msgid="5432266899591255759">"Завяршыць дзеянне з дапамогай"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Завяршыць дзеянне з дапамогай %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Завяршыць дзеянне"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 92e1a86..940a0dc 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"без отметка"</string>
<string name="selected" msgid="6614607926197755875">"избрано"</string>
<string name="not_selected" msgid="410652016565864475">"не е избрано"</string>
+ <string name="in_progress" msgid="2149208189184319441">"в ход"</string>
<string name="whichApplication" msgid="5432266899591255759">"Изпълняване на действието чрез"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Завършване на действието посредством %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Изпълняване на действието"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 2a9ecf6..b3af204 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"টিকচিহ্ন দেওয়া নেই"</string>
<string name="selected" msgid="6614607926197755875">"বেছে নেওয়া হয়েছে"</string>
<string name="not_selected" msgid="410652016565864475">"বেছে নেওয়া হয়নি"</string>
+ <string name="in_progress" msgid="2149208189184319441">"কাজ চলছে"</string>
<string name="whichApplication" msgid="5432266899591255759">"এটি ব্যবহার করে ক্রিয়াকলাপ সম্পূর্ণ করুন"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ব্যবহার করে ক্রিয়াকলাপ সম্পূর্ণ করুন"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"ক্রিয়াকলাপ সম্পূর্ণ করুন"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index f811ac2..cc07577 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1198,6 +1198,7 @@
<string name="not_checked" msgid="7972320087569023342">"nije označeno"</string>
<string name="selected" msgid="6614607926197755875">"odabrano"</string>
<string name="not_selected" msgid="410652016565864475">"nije odabrano"</string>
+ <string name="in_progress" msgid="2149208189184319441">"u toku"</string>
<string name="whichApplication" msgid="5432266899591255759">"Završite radnju pomoću aplikacije"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Završite radnju pomoću aplikacije %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Izvršiti akciju"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 19762bf..b663ba6 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"no seleccionat"</string>
<string name="selected" msgid="6614607926197755875">"seleccionat"</string>
<string name="not_selected" msgid="410652016565864475">"no seleccionat"</string>
+ <string name="in_progress" msgid="2149208189184319441">"en curs"</string>
<string name="whichApplication" msgid="5432266899591255759">"Completa l\'acció mitjançant"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Completa l\'acció amb %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Completa l\'acció"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 95b2b55..568e3ac 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1218,6 +1218,7 @@
<string name="not_checked" msgid="7972320087569023342">"nevybráno"</string>
<string name="selected" msgid="6614607926197755875">"vybráno"</string>
<string name="not_selected" msgid="410652016565864475">"nevybráno"</string>
+ <string name="in_progress" msgid="2149208189184319441">"probíhá"</string>
<string name="whichApplication" msgid="5432266899591255759">"Dokončit akci pomocí aplikace"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Dokončit akci pomocí aplikace %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Dokončit akci"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 79379f1..2b949bd 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"slået fra"</string>
<string name="selected" msgid="6614607926197755875">"valgt"</string>
<string name="not_selected" msgid="410652016565864475">"ikke valgt"</string>
+ <string name="in_progress" msgid="2149208189184319441">"i gang"</string>
<string name="whichApplication" msgid="5432266899591255759">"Brug"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Gennemfør handling ved hjælp af %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Afslut handling"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index b8727e8..2357b31 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"deaktiviert"</string>
<string name="selected" msgid="6614607926197755875">"ausgewählt"</string>
<string name="not_selected" msgid="410652016565864475">"nicht ausgewählt"</string>
+ <string name="in_progress" msgid="2149208189184319441">"Noch nicht abgeschlossen"</string>
<string name="whichApplication" msgid="5432266899591255759">"Aktion durchführen mit"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Aktion mit %1$s abschließen"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Abschließen"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 2aa10e5..4900e8b 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"μη επιλεγμένο"</string>
<string name="selected" msgid="6614607926197755875">"επιλεγμένο"</string>
<string name="not_selected" msgid="410652016565864475">"μη επιλεγμένο"</string>
+ <string name="in_progress" msgid="2149208189184319441">"σε εξέλιξη"</string>
<string name="whichApplication" msgid="5432266899591255759">"Ολοκλήρωση ενέργειας με τη χρήση"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Ολοκληρωμένη ενέργεια με χρήση %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Ολοκλήρωση ενέργειας"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 0817cb5..5990ab7 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"not ticked"</string>
<string name="selected" msgid="6614607926197755875">"selected"</string>
<string name="not_selected" msgid="410652016565864475">"not selected"</string>
+ <string name="in_progress" msgid="2149208189184319441">"In progress"</string>
<string name="whichApplication" msgid="5432266899591255759">"Complete action using"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Complete action using %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Complete action"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 6e3b041..cc78a04 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"not checked"</string>
<string name="selected" msgid="6614607926197755875">"selected"</string>
<string name="not_selected" msgid="410652016565864475">"not selected"</string>
+ <string name="in_progress" msgid="2149208189184319441">"In progress"</string>
<string name="whichApplication" msgid="5432266899591255759">"Complete action using"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Complete action using %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Complete action"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 74973c4..421559d 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"not ticked"</string>
<string name="selected" msgid="6614607926197755875">"selected"</string>
<string name="not_selected" msgid="410652016565864475">"not selected"</string>
+ <string name="in_progress" msgid="2149208189184319441">"In progress"</string>
<string name="whichApplication" msgid="5432266899591255759">"Complete action using"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Complete action using %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Complete action"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 8fe3f42..b0f6109 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"not ticked"</string>
<string name="selected" msgid="6614607926197755875">"selected"</string>
<string name="not_selected" msgid="410652016565864475">"not selected"</string>
+ <string name="in_progress" msgid="2149208189184319441">"In progress"</string>
<string name="whichApplication" msgid="5432266899591255759">"Complete action using"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Complete action using %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Complete action"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 2d92123..7e389d4 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"not checked"</string>
<string name="selected" msgid="6614607926197755875">"selected"</string>
<string name="not_selected" msgid="410652016565864475">"not selected"</string>
+ <string name="in_progress" msgid="2149208189184319441">"in progress"</string>
<string name="whichApplication" msgid="5432266899591255759">"Complete action using"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Complete action using %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Complete action"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 9102b89..8848f23 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"desactivado"</string>
<string name="selected" msgid="6614607926197755875">"seleccionado"</string>
<string name="not_selected" msgid="410652016565864475">"no seleccionado"</string>
+ <string name="in_progress" msgid="2149208189184319441">"en curso"</string>
<string name="whichApplication" msgid="5432266899591255759">"Completar la acción mediante"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Completar acción con %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Completar acción"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 07f805b..5ea9a29 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"no seleccionado"</string>
<string name="selected" msgid="6614607926197755875">"seleccionado"</string>
<string name="not_selected" msgid="410652016565864475">"no seleccionado"</string>
+ <string name="in_progress" msgid="2149208189184319441">"en curso"</string>
<string name="whichApplication" msgid="5432266899591255759">"Completar acción utilizando"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Completar acción con %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Completar acción"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 937161b..1d1e959 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"märkimata"</string>
<string name="selected" msgid="6614607926197755875">"valitud"</string>
<string name="not_selected" msgid="410652016565864475">"pole valitud"</string>
+ <string name="in_progress" msgid="2149208189184319441">"pooleli"</string>
<string name="whichApplication" msgid="5432266899591255759">"Lõpetage toiming rakendusega"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Toimingu lõpetamine, kasutades rakendust %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Vii toiming lõpule"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 3160134..97e8ddf 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"markatu gabe"</string>
<string name="selected" msgid="6614607926197755875">"hautatuta"</string>
<string name="not_selected" msgid="410652016565864475">"hautatu gabe"</string>
+ <string name="in_progress" msgid="2149208189184319441">"abian"</string>
<string name="whichApplication" msgid="5432266899591255759">"Gauzatu ekintza hau erabilita:"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Osatu ekintza %1$s erabiliz"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Osatu ekintza"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 3787d4c..5583bbf 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"بدون علامت"</string>
<string name="selected" msgid="6614607926197755875">"انتخاب شده"</string>
<string name="not_selected" msgid="410652016565864475">"انتخاب نشده"</string>
+ <string name="in_progress" msgid="2149208189184319441">"درحال انجام"</string>
<string name="whichApplication" msgid="5432266899591255759">"تکمیل کنش بااستفاده از"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"تکمیل کنش بااستفاده از %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"تکمیل عملکرد"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 965970c..7a06410 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"ei valittu"</string>
<string name="selected" msgid="6614607926197755875">"valittu"</string>
<string name="not_selected" msgid="410652016565864475">"ei valittu"</string>
+ <string name="in_progress" msgid="2149208189184319441">"käynnissä"</string>
<string name="whichApplication" msgid="5432266899591255759">"Tee toiminto käyttäen sovellusta"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Suorita sovelluksella %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Suorita toiminto"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 44754d7..f312b3a 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"non coché"</string>
<string name="selected" msgid="6614607926197755875">"sélectionné"</string>
<string name="not_selected" msgid="410652016565864475">"non sélectionné"</string>
+ <string name="in_progress" msgid="2149208189184319441">"en cours"</string>
<string name="whichApplication" msgid="5432266899591255759">"Continuer avec"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Continuer avec %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Terminer l\'action"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index cc22d83..f9c8e47 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"désactivé"</string>
<string name="selected" msgid="6614607926197755875">"sélectionné"</string>
<string name="not_selected" msgid="410652016565864475">"non sélectionné"</string>
+ <string name="in_progress" msgid="2149208189184319441">"en cours"</string>
<string name="whichApplication" msgid="5432266899591255759">"Continuer avec"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Terminer l\'action avec %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Terminer l\'action"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index ba41c75..1180f27 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"non seleccionado"</string>
<string name="selected" msgid="6614607926197755875">"elemento seleccionado"</string>
<string name="not_selected" msgid="410652016565864475">"elemento non seleccionado"</string>
+ <string name="in_progress" msgid="2149208189184319441">"en curso"</string>
<string name="whichApplication" msgid="5432266899591255759">"Completar a acción usando"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Completar a acción usando %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Completar acción"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 4157e1f..12ec1fa 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"ચેક કર્યું નથી"</string>
<string name="selected" msgid="6614607926197755875">"પસંદ કરેલી"</string>
<string name="not_selected" msgid="410652016565864475">"પસંદ નહીં કરેલી"</string>
+ <string name="in_progress" msgid="2149208189184319441">"પ્રક્રિયા ચાલુ છે"</string>
<string name="whichApplication" msgid="5432266899591255759">"આના ઉપયોગથી ક્રિયા પૂર્ણ કરો"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ઉપયોગથી ક્રિયા પૂર્ણ કરો"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"ક્રિયા પૂર્ણ કરો"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 6e8bd94..22af050 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"बंद है"</string>
<string name="selected" msgid="6614607926197755875">"चुना गया"</string>
<string name="not_selected" msgid="410652016565864475">"नहीं चुना गया"</string>
+ <string name="in_progress" msgid="2149208189184319441">"जारी है"</string>
<string name="whichApplication" msgid="5432266899591255759">"इसका इस्तेमाल करके कार्रवाई को पूरा करें"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s का उपयोग करके कार्रवाई पूरी करें"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"कार्रवाई पूरी करें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 98acb89..fab138e 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1198,6 +1198,7 @@
<string name="not_checked" msgid="7972320087569023342">"nije potvrđeno"</string>
<string name="selected" msgid="6614607926197755875">"odabrano"</string>
<string name="not_selected" msgid="410652016565864475">"nije odabrano"</string>
+ <string name="in_progress" msgid="2149208189184319441">"u tijeku"</string>
<string name="whichApplication" msgid="5432266899591255759">"Radnju dovrši pomoću stavke"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Dovršavanje radnje pomoću aplikacije %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Dovrši radnju"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 58c3875..adfc510 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"nincs kiválasztva"</string>
<string name="selected" msgid="6614607926197755875">"kiválasztva"</string>
<string name="not_selected" msgid="410652016565864475">"nincs kiválasztva"</string>
+ <string name="in_progress" msgid="2149208189184319441">"folyamatban"</string>
<string name="whichApplication" msgid="5432266899591255759">"Művelet végrehajtása a következővel:"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Művelet elvégzése a(z) %1$s segítségével"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Művelet végrehajtása"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 860dcf9..4b70a40 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"նշված չէ"</string>
<string name="selected" msgid="6614607926197755875">"ընտրված է"</string>
<string name="not_selected" msgid="410652016565864475">"ընտրված չէ"</string>
+ <string name="in_progress" msgid="2149208189184319441">"ընթացքում է"</string>
<string name="whichApplication" msgid="5432266899591255759">"Ավարտել գործողությունը` օգտագործելով"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Եզրափակել գործողությունը՝ օգտագործելով %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Ավարտել գործողությունը"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index b8649c1..9f8640e 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"tidak dicentang"</string>
<string name="selected" msgid="6614607926197755875">"dipilih"</string>
<string name="not_selected" msgid="410652016565864475">"tidak dipilih"</string>
+ <string name="in_progress" msgid="2149208189184319441">"dalam proses"</string>
<string name="whichApplication" msgid="5432266899591255759">"Selesaikan tindakan menggunakan"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Selesaikan tindakan menggunakan %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Selesaikan tindakan"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 0cf1eff..9735efe 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"ekki valið"</string>
<string name="selected" msgid="6614607926197755875">"valið"</string>
<string name="not_selected" msgid="410652016565864475">"ekki valið"</string>
+ <string name="in_progress" msgid="2149208189184319441">"í gangi"</string>
<string name="whichApplication" msgid="5432266899591255759">"Ljúka aðgerð með"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Ljúka aðgerð með %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Ljúka aðgerð"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 29dbd76..fafd4fa 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"deselezionato"</string>
<string name="selected" msgid="6614607926197755875">"selezionato"</string>
<string name="not_selected" msgid="410652016565864475">"non selezionato"</string>
+ <string name="in_progress" msgid="2149208189184319441">"In corso"</string>
<string name="whichApplication" msgid="5432266899591255759">"Completa l\'azione con"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Completamento azione con %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Completa azione"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index dc43bde..74f842a 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1218,6 +1218,7 @@
<string name="not_checked" msgid="7972320087569023342">"לא מסומן"</string>
<string name="selected" msgid="6614607926197755875">"נבחר"</string>
<string name="not_selected" msgid="410652016565864475">"לא נבחר"</string>
+ <string name="in_progress" msgid="2149208189184319441">"בתהליך"</string>
<string name="whichApplication" msgid="5432266899591255759">"השלמת הפעולה באמצעות"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"השלמת הפעולה באמצעות %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"להשלמת הפעולה"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index b282b44..31f3f1e 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"OFF"</string>
<string name="selected" msgid="6614607926197755875">"選択済み"</string>
<string name="not_selected" msgid="410652016565864475">"未選択"</string>
+ <string name="in_progress" msgid="2149208189184319441">"進行中"</string>
<string name="whichApplication" msgid="5432266899591255759">"アプリケーションを選択"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$sを使用してアクションを完了"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"アクションを実行"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index dbc92da..73f6865 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"არ არის მონიშნული"</string>
<string name="selected" msgid="6614607926197755875">"არჩეულია"</string>
<string name="not_selected" msgid="410652016565864475">"არ არის არჩეული"</string>
+ <string name="in_progress" msgid="2149208189184319441">"მიმდინარეობს"</string>
<string name="whichApplication" msgid="5432266899591255759">"რა გამოვიყენოთ?"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"მოქმედების %1$s-ის გამოყენებით დასრულება"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"მოქმედების დასრულება"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 6d3429e..1ad0693 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"белгіленбеген"</string>
<string name="selected" msgid="6614607926197755875">"таңдалған"</string>
<string name="not_selected" msgid="410652016565864475">"таңдалмаған"</string>
+ <string name="in_progress" msgid="2149208189184319441">"орындалуда"</string>
<string name="whichApplication" msgid="5432266899591255759">"Әрекетті аяқтау"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Әрекетті %1$s қолданбасын пайдаланып аяқтау"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Әрекетті аяқтау"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 19aa889..17e070d 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"មិនបានធីក"</string>
<string name="selected" msgid="6614607926197755875">"បានជ្រើសរើស"</string>
<string name="not_selected" msgid="410652016565864475">"មិនបានជ្រើសរើសទេ"</string>
+ <string name="in_progress" msgid="2149208189184319441">"កំពុងដំណើរការ"</string>
<string name="whichApplication" msgid="5432266899591255759">"បញ្ចប់សកម្មភាពដោយប្រើ"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"បញ្ចប់សកម្មភាពដោយប្រើ %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"បញ្ចប់សកម្មភាព"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index a3e0ca5..d6d6fa52 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"ಪರಿಶೀಲಿಸಲಾಗಿಲ್ಲ"</string>
<string name="selected" msgid="6614607926197755875">"ಆಯ್ಕೆಮಾಡಲಾಗಿದೆ"</string>
<string name="not_selected" msgid="410652016565864475">"ಆಯ್ಕೆಮಾಡಲಾಗಿಲ್ಲ"</string>
+ <string name="in_progress" msgid="2149208189184319441">"ಪ್ರಗತಿಯಲ್ಲಿದೆ"</string>
<string name="whichApplication" msgid="5432266899591255759">"ಇದನ್ನು ಬಳಸಿಕೊಂಡು ಕ್ರಿಯೆಯನ್ನು ಪೂರ್ಣಗೊಳಿಸಿ"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ಬಳಸಿಕೊಂಡು ಕ್ರಿಯೆಯನ್ನು ಪೂರ್ಣಗೊಳಿಸಿ"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"ಕ್ರಿಯೆಯನ್ನು ಪೂರ್ಣಗೊಳಿಸಿ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 4c1691b..8a9b36f7 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"선택 안함"</string>
<string name="selected" msgid="6614607926197755875">"선택됨"</string>
<string name="not_selected" msgid="410652016565864475">"선택되지 않음"</string>
+ <string name="in_progress" msgid="2149208189184319441">"진행 중"</string>
<string name="whichApplication" msgid="5432266899591255759">"작업을 수행할 때 사용하는 애플리케이션"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s을(를) 사용하여 작업 완료"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"작업 완료"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 66ec9aa..4898f76 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"белгилене элек"</string>
<string name="selected" msgid="6614607926197755875">"тандалган"</string>
<string name="not_selected" msgid="410652016565864475">"тандалган жок"</string>
+ <string name="in_progress" msgid="2149208189184319441">"аткарылууда"</string>
<string name="whichApplication" msgid="5432266899591255759">"Кайсынысын колдоносуз?"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s аркылуу аракетти аягына чейин чыгаруу"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Аракетти аягына чыгаруу"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index fd83eda..02da756 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"ບໍ່ໄດ້ໝາຍຖືກ"</string>
<string name="selected" msgid="6614607926197755875">"ເລືອກແລ້ວ"</string>
<string name="not_selected" msgid="410652016565864475">"ບໍ່ໄດ້ເລືອກແລ້ວ"</string>
+ <string name="in_progress" msgid="2149208189184319441">"ກຳລັງດຳເນີນການ"</string>
<string name="whichApplication" msgid="5432266899591255759">"ດຳເນີນການໂດຍໃຊ້"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"ສຳເລັດການດຳເນີນການໂດຍໃຊ້ %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"ສຳເລັດຄຳສັ່ງ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index ca43ce7..1bb35da 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1218,6 +1218,7 @@
<string name="not_checked" msgid="7972320087569023342">"nepažymėta"</string>
<string name="selected" msgid="6614607926197755875">"pasirinkta"</string>
<string name="not_selected" msgid="410652016565864475">"nepasirinkta"</string>
+ <string name="in_progress" msgid="2149208189184319441">"vykdoma"</string>
<string name="whichApplication" msgid="5432266899591255759">"Užbaigti veiksmą naudojant"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Užbaigti veiksmą naudojant %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Užbaigti veiksmą"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 47094a4..78546ed 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1198,6 +1198,7 @@
<string name="not_checked" msgid="7972320087569023342">"nav atzīmēts"</string>
<string name="selected" msgid="6614607926197755875">"atlasīts"</string>
<string name="not_selected" msgid="410652016565864475">"nav atlasīts"</string>
+ <string name="in_progress" msgid="2149208189184319441">"notiek apstrāde"</string>
<string name="whichApplication" msgid="5432266899591255759">"Izvēlieties lietotni"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Pabeigt darbību, izmantojot %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Pabeigt darbību"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 5a9486e..4649fb4 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"не е штиклирано"</string>
<string name="selected" msgid="6614607926197755875">"избрано"</string>
<string name="not_selected" msgid="410652016565864475">"не е избрано"</string>
+ <string name="in_progress" msgid="2149208189184319441">"во тек"</string>
<string name="whichApplication" msgid="5432266899591255759">"Активирај со"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Остварете го дејството со %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Заврши го дејството"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 9ca392a..bd4c823 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"പരിശോധിക്കാത്തത്"</string>
<string name="selected" msgid="6614607926197755875">"തിരഞ്ഞെടുത്തു"</string>
<string name="not_selected" msgid="410652016565864475">"തിരഞ്ഞെടുത്തിട്ടില്ല"</string>
+ <string name="in_progress" msgid="2149208189184319441">"പുരോഗതിയിലാണ്"</string>
<string name="whichApplication" msgid="5432266899591255759">"പൂർണ്ണമായ പ്രവർത്തനം ഉപയോഗിക്കുന്നു"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ഉപയോഗിച്ച് പ്രവർത്തനം പൂർത്തിയാക്കുക"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"പ്രവർത്തനം പൂർത്തിയാക്കുക"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 4c01871d..805e42c 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"тэмдэглээгүй"</string>
<string name="selected" msgid="6614607926197755875">"сонгосон"</string>
<string name="not_selected" msgid="410652016565864475">"сонгоогүй"</string>
+ <string name="in_progress" msgid="2149208189184319441">"үргэлжилж байна"</string>
<string name="whichApplication" msgid="5432266899591255759">"Үйлдлийг дуусгах"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ашиглан үйлдлийг гүйцээх"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Үйлдлийг дуусгах"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 1dfa5de..245ec6d 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"तपासले नाही"</string>
<string name="selected" msgid="6614607926197755875">"निवडला"</string>
<string name="not_selected" msgid="410652016565864475">"निवडला नाही"</string>
+ <string name="in_progress" msgid="2149208189184319441">"प्रगतीपथावर आहे"</string>
<string name="whichApplication" msgid="5432266899591255759">"याचा वापर करून क्रिया पूर्ण करा"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s वापरून क्रिया पूर्ण करा"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"क्रिया पूर्ण झाली"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 2960ca2..94d932c 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"tidak ditandai"</string>
<string name="selected" msgid="6614607926197755875">"dipilih"</string>
<string name="not_selected" msgid="410652016565864475">"tidak dipilih"</string>
+ <string name="in_progress" msgid="2149208189184319441">"dalam proses"</string>
<string name="whichApplication" msgid="5432266899591255759">"Selesaikan tindakan menggunakan"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Selesaikan tindakan menggunakan %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Selesaikan tindakan"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 2c0ed7b..e952b27 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"ခြစ် မထား"</string>
<string name="selected" msgid="6614607926197755875">"ရွေးချယ်ထားသည်"</string>
<string name="not_selected" msgid="410652016565864475">"ရွေးချယ်မထားပါ"</string>
+ <string name="in_progress" msgid="2149208189184319441">"ဆောင်ရွက်နေသည်"</string>
<string name="whichApplication" msgid="5432266899591255759">"အောက်ပါတို့ကို အသုံးပြုမှု အပြီးသတ်ခြင်း"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ကို သုံးပြီး လုပ်ဆောင်ချက် ပြီးဆုံးပါစေ"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"လုပ်ဆောင်ချက်ကို အပြီးသတ်ပါ"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 0ceebf1..09f4df2 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"ikke avmerket"</string>
<string name="selected" msgid="6614607926197755875">"valgt"</string>
<string name="not_selected" msgid="410652016565864475">"ikke valgt"</string>
+ <string name="in_progress" msgid="2149208189184319441">"pågår"</string>
<string name="whichApplication" msgid="5432266899591255759">"Fullfør med"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Fullfør handlingen med %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Fullfør handlingen"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 8e7ca71..87f9bfa 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"जाँच गरिएको छैन"</string>
<string name="selected" msgid="6614607926197755875">"चयन गरियो"</string>
<string name="not_selected" msgid="410652016565864475">"चयन गरिएन"</string>
+ <string name="in_progress" msgid="2149208189184319441">"जारी छ"</string>
<string name="whichApplication" msgid="5432266899591255759">"प्रयोग गरेर कारबाही पुरा गर्नुहोस्"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"निम्न एपको प्रयोग गरी कारबाही पुरा गर्नुहोस्: %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"पूर्ण कारबाही"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 9700939..99c23de1 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"niet aangevinkt"</string>
<string name="selected" msgid="6614607926197755875">"geselecteerd"</string>
<string name="not_selected" msgid="410652016565864475">"niet geselecteerd"</string>
+ <string name="in_progress" msgid="2149208189184319441">"bezig"</string>
<string name="whichApplication" msgid="5432266899591255759">"Actie voltooien met"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Actie voltooien via %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Actie voltooien"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index d6b1e1e..db6c819 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"ଯାଞ୍ଚ ହୋଇନାହିଁ"</string>
<string name="selected" msgid="6614607926197755875">"ଚୟନ କରାଯାଇଛି"</string>
<string name="not_selected" msgid="410652016565864475">"ଚୟନ କରାଯାଇନାହିଁ"</string>
+ <string name="in_progress" msgid="2149208189184319441">"ଚାଲୁଅଛି"</string>
<string name="whichApplication" msgid="5432266899591255759">"ବ୍ୟବହାର କରି କାର୍ଯ୍ୟ ସମ୍ପୂର୍ଣ୍ଣ କରନ୍ତୁ"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ବ୍ୟବହାର କରି କାର୍ଯ୍ୟ ସମ୍ପୂର୍ଣ୍ଣ କରନ୍ତୁ"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"କାର୍ଯ୍ୟ ସମ୍ପୂର୍ଣ୍ଣ କରନ୍ତୁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 9a73f9b..9bbfda1 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"ਨਿਸ਼ਾਨਬੱਧ ਨਹੀਂ ਕੀਤਾ ਗਿਆ"</string>
<string name="selected" msgid="6614607926197755875">"ਚੁਣਿਆ ਗਿਆ"</string>
<string name="not_selected" msgid="410652016565864475">"ਨਹੀਂ ਚੁਣਿਆ ਗਿਆ"</string>
+ <string name="in_progress" msgid="2149208189184319441">"ਜਾਰੀ"</string>
<string name="whichApplication" msgid="5432266899591255759">"ਇਸਨੂੰ ਵਰਤਦੇ ਹੋਏ ਕਾਰਵਾਈ ਪੂਰੀ ਕਰੋ"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ਵਰਤਦੇ ਹੋਏ ਕਾਰਵਾਈ ਪੂਰੀ ਕਰੋ"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"ਕਾਰਵਾਈ ਪੂਰੀ ਕਰੋ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 2781df7..fdc039f 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1218,6 +1218,7 @@
<string name="not_checked" msgid="7972320087569023342">"nie wybrano"</string>
<string name="selected" msgid="6614607926197755875">"wybrano"</string>
<string name="not_selected" msgid="410652016565864475">"nie wybrano"</string>
+ <string name="in_progress" msgid="2149208189184319441">"w toku"</string>
<string name="whichApplication" msgid="5432266899591255759">"Wykonaj czynność przez..."</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Wykonaj czynność w aplikacji %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Wykonaj działanie"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index a60cb23..07ebaa3 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"não marcado"</string>
<string name="selected" msgid="6614607926197755875">"selecionado"</string>
<string name="not_selected" msgid="410652016565864475">"não selecionado"</string>
+ <string name="in_progress" msgid="2149208189184319441">"em andamento"</string>
<string name="whichApplication" msgid="5432266899591255759">"Complete a ação usando"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Concluir a ação usando %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Concluir ação"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 5f10462..356b993 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"não selecionado"</string>
<string name="selected" msgid="6614607926197755875">"selecionado"</string>
<string name="not_selected" msgid="410652016565864475">"não selecionado"</string>
+ <string name="in_progress" msgid="2149208189184319441">"em curso"</string>
<string name="whichApplication" msgid="5432266899591255759">"Concluir ação utilizando"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Concluir ação utilizando %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Concluir ação"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index a60cb23..07ebaa3 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"não marcado"</string>
<string name="selected" msgid="6614607926197755875">"selecionado"</string>
<string name="not_selected" msgid="410652016565864475">"não selecionado"</string>
+ <string name="in_progress" msgid="2149208189184319441">"em andamento"</string>
<string name="whichApplication" msgid="5432266899591255759">"Complete a ação usando"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Concluir a ação usando %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Concluir ação"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index dc351a2..bdd4eeee 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1198,6 +1198,7 @@
<string name="not_checked" msgid="7972320087569023342">"nebifat"</string>
<string name="selected" msgid="6614607926197755875">"selectat"</string>
<string name="not_selected" msgid="410652016565864475">"neselectat"</string>
+ <string name="in_progress" msgid="2149208189184319441">"în curs"</string>
<string name="whichApplication" msgid="5432266899591255759">"Finalizare acțiune utilizând"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Finalizați acțiunea utilizând %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Finalizați acțiunea"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index ea9e292..f6fa43f 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1218,6 +1218,7 @@
<string name="not_checked" msgid="7972320087569023342">"не отмечено"</string>
<string name="selected" msgid="6614607926197755875">"выбрано"</string>
<string name="not_selected" msgid="410652016565864475">"не выбрано"</string>
+ <string name="in_progress" msgid="2149208189184319441">"в процессе"</string>
<string name="whichApplication" msgid="5432266899591255759">"Что использовать?"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Выполнить с помощью приложения \"%1$s\""</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Выполнить действие"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 6064103..6c7557c 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"පරීක්ෂා කර නැත"</string>
<string name="selected" msgid="6614607926197755875">"තෝරන ලදි"</string>
<string name="not_selected" msgid="410652016565864475">"තෝරා නොමැත"</string>
+ <string name="in_progress" msgid="2149208189184319441">"සිදු වෙමින් පවතී"</string>
<string name="whichApplication" msgid="5432266899591255759">"පහත භාවිතයෙන් ක්රියාව සම්පූර්ණ කරන්න"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s භාවිතා කරමින් ක්රියාව සම්පුර්ණ කරන්න"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"ක්රියාව සම්පූර්ණ කරන්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index e42cc01..0e2455c 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1218,6 +1218,7 @@
<string name="not_checked" msgid="7972320087569023342">"nezačiarknuté"</string>
<string name="selected" msgid="6614607926197755875">"vybrané"</string>
<string name="not_selected" msgid="410652016565864475">"nevybrané"</string>
+ <string name="in_progress" msgid="2149208189184319441">"prebieha"</string>
<string name="whichApplication" msgid="5432266899591255759">"Dokončiť akciu pomocou aplikácie"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Dokončiť akciu pomocou aplikácie %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Dokončiť akciu"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 506c066..4fa698e 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1218,6 +1218,7 @@
<string name="not_checked" msgid="7972320087569023342">"ni potrjeno"</string>
<string name="selected" msgid="6614607926197755875">"izbrano"</string>
<string name="not_selected" msgid="410652016565864475">"ni izbrano"</string>
+ <string name="in_progress" msgid="2149208189184319441">"v teku"</string>
<string name="whichApplication" msgid="5432266899591255759">"Dokončanje dejanja z aplikacijo"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Dokončanje dejanja z aplikacijo %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Izvedba dejanja"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index ed6a616..30cdee9 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"nuk u përzgjodh"</string>
<string name="selected" msgid="6614607926197755875">"i zgjedhur"</string>
<string name="not_selected" msgid="410652016565864475">"i pazgjedhur"</string>
+ <string name="in_progress" msgid="2149208189184319441">"në vazhdim"</string>
<string name="whichApplication" msgid="5432266899591255759">"Përfundo veprimin duke përdorur"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Përfundo veprimin duke përdorur %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Përfundo veprimin"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 14f5673..a274bd0 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1198,6 +1198,7 @@
<string name="not_checked" msgid="7972320087569023342">"није означено"</string>
<string name="selected" msgid="6614607926197755875">"изабрано"</string>
<string name="not_selected" msgid="410652016565864475">"није изабрано"</string>
+ <string name="in_progress" msgid="2149208189184319441">"у току"</string>
<string name="whichApplication" msgid="5432266899591255759">"Доврши радњу преко"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Завршите радњу помоћу апликације %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Заврши радњу"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index f5e1f81..d983fc6 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"inte markerad"</string>
<string name="selected" msgid="6614607926197755875">"valt"</string>
<string name="not_selected" msgid="410652016565864475">"inte valt"</string>
+ <string name="in_progress" msgid="2149208189184319441">"pågår"</string>
<string name="whichApplication" msgid="5432266899591255759">"Slutför åtgärd genom att använda"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Slutför åtgärden med %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Slutför åtgärd"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index c05f866..685cc89 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"haijateuliwa"</string>
<string name="selected" msgid="6614607926197755875">"imechaguliwa"</string>
<string name="not_selected" msgid="410652016565864475">"haijachaguliwa"</string>
+ <string name="in_progress" msgid="2149208189184319441">"inaendelea"</string>
<string name="whichApplication" msgid="5432266899591255759">"Kamilisha kitendo ukitumia"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Kamilisha kitendo ukitumia %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Kamilisha kitendo"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 9be38c9..ba4d329 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"முடக்கப்பட்டுள்ளது"</string>
<string name="selected" msgid="6614607926197755875">"தேர்ந்தெடுக்கப்பட்டது"</string>
<string name="not_selected" msgid="410652016565864475">"தேர்ந்தெடுக்கப்படவில்லை"</string>
+ <string name="in_progress" msgid="2149208189184319441">"செயலிலுள்ளது"</string>
<string name="whichApplication" msgid="5432266899591255759">"இதைப் பயன்படுத்தி செயலை நிறைவுசெய்"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ஐப் பயன்படுத்தி செயலை முடிக்கவும்"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"செயலை முடி"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index d910224..b1a4c86 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"ఎంచుకోలేదు"</string>
<string name="selected" msgid="6614607926197755875">"ఎంచుకోబడింది"</string>
<string name="not_selected" msgid="410652016565864475">"ఎంచుకోబడలేదు"</string>
+ <string name="in_progress" msgid="2149208189184319441">"ప్రోగ్రెస్లో ఉంది"</string>
<string name="whichApplication" msgid="5432266899591255759">"దీన్ని ఉపయోగించి చర్యను పూర్తి చేయండి"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$sను ఉపయోగించి చర్యను పూర్తి చేయి"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"చర్యను పూర్తి చేయి"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index b6c6ed5c2..8d4cff9 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"ยังไม่เลือก"</string>
<string name="selected" msgid="6614607926197755875">"เลือกไว้"</string>
<string name="not_selected" msgid="410652016565864475">"ไม่ได้เลือกไว้"</string>
+ <string name="in_progress" msgid="2149208189184319441">"กำลังดำเนินการ"</string>
<string name="whichApplication" msgid="5432266899591255759">"ทำงานให้เสร็จโดยใช้"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"ดำเนินการให้เสร็จสมบูรณ์โดยใช้ %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"ทำงานให้เสร็จสิ้น"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 4ea1da9..a19b3d6 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"hindi nilagyan ng check"</string>
<string name="selected" msgid="6614607926197755875">"pinili"</string>
<string name="not_selected" msgid="410652016565864475">"hindi pinili"</string>
+ <string name="in_progress" msgid="2149208189184319441">"isinasagawa"</string>
<string name="whichApplication" msgid="5432266899591255759">"Kumpletuhin ang pagkilos gamit ang"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Tapusin ang pagkilos gamit ang %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Gawin ang pagkilos"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 306fc82..145e00ac 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"işaretli değil"</string>
<string name="selected" msgid="6614607926197755875">"seçili"</string>
<string name="not_selected" msgid="410652016565864475">"seçili değil"</string>
+ <string name="in_progress" msgid="2149208189184319441">"devam ediyor"</string>
<string name="whichApplication" msgid="5432266899591255759">"İşlemi şunu kullanarak tamamla"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"İşlemi %1$s kullanarak tamamla"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"İşlemi tamamla"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index baf4a44..5b4cc71 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1218,6 +1218,7 @@
<string name="not_checked" msgid="7972320087569023342">"не вибрано"</string>
<string name="selected" msgid="6614607926197755875">"вибрано"</string>
<string name="not_selected" msgid="410652016565864475">"не вибрано"</string>
+ <string name="in_progress" msgid="2149208189184319441">"триває"</string>
<string name="whichApplication" msgid="5432266899591255759">"Що використовувати?"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Завершити дію за допомогою %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Завершити дію"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 8318021..6eb2598 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"چیک نہیں کیا گیا"</string>
<string name="selected" msgid="6614607926197755875">"منتخب کردہ"</string>
<string name="not_selected" msgid="410652016565864475">"غیر منتخب کردہ"</string>
+ <string name="in_progress" msgid="2149208189184319441">"جاری ہے"</string>
<string name="whichApplication" msgid="5432266899591255759">"اس کا استعمال کرکے کارروائی مکمل کریں"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s کا استعمال کر کے کارروائی مکمل کریں"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"کارروائی مکمل کریں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 4c30b54..af6d2f2 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"belgilanmadi"</string>
<string name="selected" msgid="6614607926197755875">"tanlangan"</string>
<string name="not_selected" msgid="410652016565864475">"tanlanmagan"</string>
+ <string name="in_progress" msgid="2149208189184319441">"bajarilmoqda"</string>
<string name="whichApplication" msgid="5432266899591255759">"Nima ishlatilsin?"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"“%1$s” bilan ochish"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Amalni bajarish"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 61aba14..ea718d6 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"chưa chọn"</string>
<string name="selected" msgid="6614607926197755875">"đã chọn"</string>
<string name="not_selected" msgid="410652016565864475">"chưa được chọn"</string>
+ <string name="in_progress" msgid="2149208189184319441">"đang thực hiện"</string>
<string name="whichApplication" msgid="5432266899591255759">"Hoàn tất thao tác bằng"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Hoàn tất thao tác bằng %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Hoàn thành tác vụ"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 7fefe74..d8c7826 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"未勾选"</string>
<string name="selected" msgid="6614607926197755875">"已选择"</string>
<string name="not_selected" msgid="410652016565864475">"未选择"</string>
+ <string name="in_progress" msgid="2149208189184319441">"进行中"</string>
<string name="whichApplication" msgid="5432266899591255759">"选择要使用的应用:"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"使用%1$s完成操作"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"完成操作"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 457f0d5..3b626d8 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"未勾選"</string>
<string name="selected" msgid="6614607926197755875">"揀咗"</string>
<string name="not_selected" msgid="410652016565864475">"未揀"</string>
+ <string name="in_progress" msgid="2149208189184319441">"處理中"</string>
<string name="whichApplication" msgid="5432266899591255759">"完成操作需使用"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"完成操作需使用 %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"完成操作"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 82be318..a0d5624 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"未勾選"</string>
<string name="selected" msgid="6614607926197755875">"已選取"</string>
<string name="not_selected" msgid="410652016565864475">"未選取"</string>
+ <string name="in_progress" msgid="2149208189184319441">"處理中"</string>
<string name="whichApplication" msgid="5432266899591255759">"選擇要使用的應用程式"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"完成操作需使用 %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"完成操作"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 85cf88c..4de389e 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1178,6 +1178,7 @@
<string name="not_checked" msgid="7972320087569023342">"akuhloliwe"</string>
<string name="selected" msgid="6614607926197755875">"okukhethiwe"</string>
<string name="not_selected" msgid="410652016565864475">"akukhethiwe"</string>
+ <string name="in_progress" msgid="2149208189184319441">"kuyaqhubeka"</string>
<string name="whichApplication" msgid="5432266899591255759">"Qedela isenzo usebenzisa"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Qedela isenzo usebenzisa i-%1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Qedela isenzo"</string>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index d052d70..441aa2d 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -285,9 +285,6 @@
<!-- Additional flag from base permission type: this permission can be automatically
granted to the system default text classifier -->
<flag name="textClassifier" value="0x10000" />
- <!-- Additional flag from base permission type: this permission can be automatically
- granted to the document manager -->
- <flag name="documenter" value="0x40000" />
<!-- Additional flag from base permission type: this permission automatically
granted to device configurator -->
<flag name="configurator" value="0x80000" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 5ac2336..e97beab 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3574,8 +3574,8 @@
-->
<integer name="config_largeScreenSmallestScreenWidthDp">600</integer>
- <!-- True if the device is using leagacy split. -->
- <bool name="config_useLegacySplit">true</bool>
+ <!-- True if the device is using legacy split. -->
+ <bool name="config_useLegacySplit">false</bool>
<!-- True if the device supports running activities on secondary displays. -->
<bool name="config_supportsMultiDisplay">true</bool>
@@ -4857,13 +4857,21 @@
<bool name="config_cecHdmiCecVersion_userConfigurable">true</bool>
<bool name="config_cecHdmiCecVersion14b_allowed">true</bool>
- <bool name="config_cecHdmiCecVersion14b_default">true</bool>
+ <bool name="config_cecHdmiCecVersion14b_default">false</bool>
<bool name="config_cecHdmiCecVersion20_allowed">true</bool>
- <bool name="config_cecHdmiCecVersion20_default">false</bool>
+ <bool name="config_cecHdmiCecVersion20_default">true</bool>
+
+ <bool name="config_cecRoutingControl_userConfigurable">true</bool>
+ <bool name="config_cecRoutingControlEnabled_allowed">true</bool>
+ <bool name="config_cecRoutingControlEnabled_default">false</bool>
+ <bool name="config_cecRoutingControlDisabled_allowed">true</bool>
+ <bool name="config_cecRoutingControlDisabled_default">true</bool>
<bool name="config_cecPowerControlMode_userConfigurable">true</bool>
<bool name="config_cecPowerControlModeTv_allowed">true</bool>
- <bool name="config_cecPowerControlModeTv_default">true</bool>
+ <bool name="config_cecPowerControlModeTv_default">false</bool>
+ <bool name="config_cecPowerControlModeTvAndAudioSystem_allowed">true</bool>
+ <bool name="config_cecPowerControlModeTvAndAudioSystem_default">true</bool>
<bool name="config_cecPowerControlModeBroadcast_allowed">true</bool>
<bool name="config_cecPowerControlModeBroadcast_default">false</bool>
<bool name="config_cecPowerControlModeNone_allowed">true</bool>
@@ -4875,6 +4883,12 @@
<bool name="config_cecPowerStateChangeOnActiveSourceLostStandbyNow_allowed">true</bool>
<bool name="config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default">false</bool>
+ <bool name="config_cecSystemAudioControl_userConfigurable">true</bool>
+ <bool name="config_cecSystemAudioControlEnabled_allowed">true</bool>
+ <bool name="config_cecSystemAudioControlEnabled_default">true</bool>
+ <bool name="config_cecSystemAudioControlDisabled_allowed">true</bool>
+ <bool name="config_cecSystemAudioControlDisabled_default">false</bool>
+
<bool name="config_cecSystemAudioModeMuting_userConfigurable">true</bool>
<bool name="config_cecSystemAudioModeMutingEnabled_allowed">true</bool>
<bool name="config_cecSystemAudioModeMutingEnabled_default">true</bool>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 2403a60..a519cde 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3201,10 +3201,158 @@
<public type="string" name="config_defaultRingtoneVibrationSound" id="0x0104003b" />
<!-- ===============================================================
+ Resources added in version S-V2 of the platform
+
+ NOTE: add <public> elements within a <staging-public-group> like so:
+
+ <staging-public-group type="attr" first-id="0x01ff0000">
+ <public name="exampleAttr1" />
+ <public name="exampleAttr2" />
+ </staging-public-group>
+
+ To add a new <staging-public-group> block, find the id value for the
+ last <staging-public-group> block defined for thie API level, and
+ subtract 0x00010000 from it to get to the id of the new block.
+
+ For example, if the block closest to the end of this file has an id
+ of 0x01ee0000, the id of the new block should be 0x01ed0000
+ (0x01ee0000 - 0x00010000 = 0x01ed0000).
+ =============================================================== -->
+ <eat-comment />
+
+ <staging-public-group type="attr" first-id="0x01ff0000">
+ </staging-public-group>
+
+ <staging-public-group type="id" first-id="0x01fe0000">
+ </staging-public-group>
+
+ <staging-public-group type="style" first-id="0x01fd0000">
+ </staging-public-group>
+
+ <staging-public-group type="string" first-id="0x01fc0000">
+ </staging-public-group>
+
+ <staging-public-group type="dimen" first-id="0x01fb0000">
+ </staging-public-group>
+
+ <staging-public-group type="color" first-id="0x01fa0000">
+ </staging-public-group>
+
+ <staging-public-group type="array" first-id="0x01f90000">
+ </staging-public-group>
+
+ <staging-public-group type="drawable" first-id="0x01f80000">
+ </staging-public-group>
+
+ <staging-public-group type="layout" first-id="0x01f70000">
+ </staging-public-group>
+
+ <staging-public-group type="anim" first-id="0x01f60000">
+ </staging-public-group>
+
+ <staging-public-group type="animator" first-id="0x01f50000">
+ </staging-public-group>
+
+ <staging-public-group type="interpolator" first-id="0x01f40000">
+ </staging-public-group>
+
+ <staging-public-group type="mipmap" first-id="0x01f30000">
+ </staging-public-group>
+
+ <staging-public-group type="integer" first-id="0x01f20000">
+ </staging-public-group>
+
+ <staging-public-group type="transition" first-id="0x01f10000">
+ </staging-public-group>
+
+ <staging-public-group type="raw" first-id="0x01f00000">
+ </staging-public-group>
+
+ <staging-public-group type="bool" first-id="0x01ef0000">
+ </staging-public-group>
+
+ <staging-public-group type="fraction" first-id="0x01ee0000">
+ </staging-public-group>
+
+ <!-- ===============================================================
+ Resources added in version T of the platform
+
+ NOTE: add <public> elements within a <staging-public-group> like so:
+
+ <staging-public-group type="attr" first-id="0x01ff0000">
+ <public name="exampleAttr1" />
+ <public name="exampleAttr2" />
+ </staging-public-group>
+
+ To add a new <staging-public-group> block, find the id value for the
+ last <staging-public-group> block defined for thie API level, and
+ subtract 0x00010000 from it to get to the id of the new block.
+
+ For example, if the block closest to the end of this file has an id
+ of 0x01ee0000, the id of the new block should be 0x01ed0000
+ (0x01ee0000 - 0x00010000 = 0x01ed0000).
+ =============================================================== -->
+ <eat-comment />
+
+ <staging-public-group type="attr" first-id="0x01df0000">
+ </staging-public-group>
+
+ <staging-public-group type="id" first-id="0x01de0000">
+ </staging-public-group>
+
+ <staging-public-group type="style" first-id="0x0dfd0000">
+ </staging-public-group>
+
+ <staging-public-group type="string" first-id="0x0dfc0000">
+ </staging-public-group>
+
+ <staging-public-group type="dimen" first-id="0x01db0000">
+ </staging-public-group>
+
+ <staging-public-group type="color" first-id="0x01da0000">
+ </staging-public-group>
+
+ <staging-public-group type="array" first-id="0x01d90000">
+ </staging-public-group>
+
+ <staging-public-group type="drawable" first-id="0x01d80000">
+ </staging-public-group>
+
+ <staging-public-group type="layout" first-id="0x01d70000">
+ </staging-public-group>
+
+ <staging-public-group type="anim" first-id="0x01d60000">
+ </staging-public-group>
+
+ <staging-public-group type="animator" first-id="0x01d50000">
+ </staging-public-group>
+
+ <staging-public-group type="interpolator" first-id="0x01d40000">
+ </staging-public-group>
+
+ <staging-public-group type="mipmap" first-id="0x01d30000">
+ </staging-public-group>
+
+ <staging-public-group type="integer" first-id="0x01d20000">
+ </staging-public-group>
+
+ <staging-public-group type="transition" first-id="0x01d10000">
+ </staging-public-group>
+
+ <staging-public-group type="raw" first-id="0x01d00000">
+ </staging-public-group>
+
+ <staging-public-group type="bool" first-id="0x01cf0000">
+ </staging-public-group>
+
+ <staging-public-group type="fraction" first-id="0x01ce0000">
+ </staging-public-group>
+
+ <!-- ===============================================================
DO NOT ADD UN-GROUPED ITEMS HERE
Any new items (attrs, styles, ids, etc.) *must* be added in a
- public-group block, as the preceding comment explains.
+ staging-public-group block, as the preceding comment explains.
Items added outside of a group may have their value recalculated
every time something new is added to this file.
=============================================================== -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index bdeff893..186605d 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3190,6 +3190,9 @@
<!-- Default not selected text used by accessibility for an element that can be selected or unselected. [CHAR LIMIT=NONE] -->
<string name="not_selected">not selected</string>
+ <!-- Default state description for indeterminate progressbar. [CHAR LIMIT=NONE] -->
+ <string name="in_progress">in progress</string>
+
<!-- Title of intent resolver dialog when selecting an application to run. -->
<string name="whichApplication">Complete action using</string>
<!-- Title of intent resolver dialog when selecting an application to run
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b574415..9b50737 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -794,6 +794,7 @@
<java-symbol type="string" name="ime_action_previous" />
<java-symbol type="string" name="ime_action_search" />
<java-symbol type="string" name="ime_action_send" />
+ <java-symbol type="string" name="in_progress" />
<java-symbol type="string" name="invalidPin" />
<java-symbol type="string" name="js_dialog_before_unload_positive_button" />
<java-symbol type="string" name="js_dialog_before_unload_negative_button" />
@@ -4291,9 +4292,17 @@
<java-symbol type="bool" name="config_cecHdmiCecVersion20_allowed" />
<java-symbol type="bool" name="config_cecHdmiCecVersion20_default" />
+ <java-symbol type="bool" name="config_cecRoutingControl_userConfigurable" />
+ <java-symbol type="bool" name="config_cecRoutingControlEnabled_allowed" />
+ <java-symbol type="bool" name="config_cecRoutingControlEnabled_default" />
+ <java-symbol type="bool" name="config_cecRoutingControlDisabled_allowed" />
+ <java-symbol type="bool" name="config_cecRoutingControlDisabled_default" />
+
<java-symbol type="bool" name="config_cecPowerControlMode_userConfigurable" />
<java-symbol type="bool" name="config_cecPowerControlModeTv_allowed" />
<java-symbol type="bool" name="config_cecPowerControlModeTv_default" />
+ <java-symbol type="bool" name="config_cecPowerControlModeTvAndAudioSystem_allowed" />
+ <java-symbol type="bool" name="config_cecPowerControlModeTvAndAudioSystem_default" />
<java-symbol type="bool" name="config_cecPowerControlModeBroadcast_allowed" />
<java-symbol type="bool" name="config_cecPowerControlModeBroadcast_default" />
<java-symbol type="bool" name="config_cecPowerControlModeNone_allowed" />
@@ -4305,6 +4314,12 @@
<java-symbol type="bool" name="config_cecPowerStateChangeOnActiveSourceLostStandbyNow_allowed" />
<java-symbol type="bool" name="config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default" />
+ <java-symbol type="bool" name="config_cecSystemAudioControl_userConfigurable" />
+ <java-symbol type="bool" name="config_cecSystemAudioControlEnabled_allowed" />
+ <java-symbol type="bool" name="config_cecSystemAudioControlEnabled_default" />
+ <java-symbol type="bool" name="config_cecSystemAudioControlDisabled_allowed" />
+ <java-symbol type="bool" name="config_cecSystemAudioControlDisabled_default" />
+
<java-symbol type="bool" name="config_cecSystemAudioModeMuting_userConfigurable" />
<java-symbol type="bool" name="config_cecSystemAudioModeMutingEnabled_allowed" />
<java-symbol type="bool" name="config_cecSystemAudioModeMutingEnabled_default" />
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 2650d9f..2be5c47 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -245,6 +245,9 @@
<!-- Slovakia: 4 digits (premium), plus EU: http://www.cmtelecom.com/premium-sms/slovakia -->
<shortcode country="sk" premium="\\d{4}" free="116\\d{3}|8000" />
+ <!-- Taiwan -->
+ <shortcode country="tw" pattern="\\d{4}" free="1922" />
+
<!-- Thailand: 4186001 used by AIS_TH_DCB -->
<shortcode country="th" pattern="\\d{1,5}" premium="4\\d{6}" free="4186001" />
diff --git a/core/tests/coretests/src/android/accessibilityservice/AccessibilityServiceTest.java b/core/tests/coretests/src/android/accessibilityservice/AccessibilityServiceTest.java
index c65ef9a..53ba140 100644
--- a/core/tests/coretests/src/android/accessibilityservice/AccessibilityServiceTest.java
+++ b/core/tests/coretests/src/android/accessibilityservice/AccessibilityServiceTest.java
@@ -16,18 +16,40 @@
package android.accessibilityservice;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
+import android.content.Context;
import android.content.Intent;
+import android.graphics.PixelFormat;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.media.ImageReader;
+import android.os.Binder;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
+import android.util.SparseArray;
+import android.view.Display;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityEvent;
+import android.window.WindowTokenClient;
import androidx.test.InstrumentationRegistry;
+import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -42,6 +64,8 @@
public class AccessibilityServiceTest {
private static final String TAG = "AccessibilityServiceTest";
private static final int CONNECTION_ID = 1;
+ private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams(
+ TYPE_ACCESSIBILITY_OVERLAY);
private static class AccessibilityServiceTestClass extends AccessibilityService {
private IAccessibilityServiceClient mCallback;
@@ -49,7 +73,11 @@
AccessibilityServiceTestClass() {
super();
- attachBaseContext(InstrumentationRegistry.getContext());
+ Context context = ApplicationProvider.getApplicationContext();
+ final Display display = context.getSystemService(DisplayManager.class)
+ .getDisplay(DEFAULT_DISPLAY);
+
+ attachBaseContext(context.createTokenContext(new WindowTokenClient(), display));
mLooper = InstrumentationRegistry.getContext().getMainLooper();
}
@@ -78,14 +106,33 @@
private @Mock IBinder mMockIBinder;
private IAccessibilityServiceClient mServiceInterface;
private AccessibilityServiceTestClass mService;
+ private final SparseArray<IBinder> mWindowTokens = new SparseArray<>();
@Before
- public void setUp() throws RemoteException {
+ public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mService = new AccessibilityServiceTestClass();
+ mService.onCreate();
mService.setupCallback(mMockClientForCallback);
mServiceInterface = (IAccessibilityServiceClient) mService.onBind(new Intent());
mServiceInterface.init(mMockConnection, CONNECTION_ID, mMockIBinder);
+ doAnswer(invocation -> {
+ Object[] args = invocation.getArguments();
+ final int displayId = (int) args[0];
+ final IBinder token = new Binder();
+ WindowManagerGlobal.getWindowManagerService().addWindowToken(token,
+ TYPE_ACCESSIBILITY_OVERLAY, displayId, null /* options */);
+ mWindowTokens.put(displayId, token);
+ return token;
+ }).when(mMockConnection).getOverlayWindowToken(anyInt());
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ for (int i = mWindowTokens.size() - 1; i >= 0; --i) {
+ WindowManagerGlobal.getWindowManagerService().removeWindowToken(
+ mWindowTokens.valueAt(i), mWindowTokens.keyAt(i));
+ }
}
@Test
@@ -101,4 +148,79 @@
verify(mMockConnection).getSystemActions();
}
+
+ @Test
+ public void testAddViewWithA11yServiceDerivedDisplayContext() throws Exception {
+ try (VirtualDisplaySession session = new VirtualDisplaySession()) {
+ final Context context = mService.createDisplayContext(session.getDisplay());
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(
+ () -> context.getSystemService(WindowManager.class)
+ .addView(new View(context), mParams)
+ );
+ }
+ }
+
+ @Test
+ public void testAddViewWithA11yServiceDerivedWindowContext() throws Exception {
+ try (VirtualDisplaySession session = new VirtualDisplaySession()) {
+ final Context context = mService.createDisplayContext(session.getDisplay())
+ .createWindowContext(TYPE_ACCESSIBILITY_OVERLAY, null /* options */);
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(
+ () -> context.getSystemService(WindowManager.class)
+ .addView(new View(context), mParams)
+ );
+ }
+ }
+
+ @Test
+ public void testAddViewWithA11yServiceDerivedWindowContextWithDisplay() throws Exception {
+ try (VirtualDisplaySession session = new VirtualDisplaySession()) {
+ final Context context = mService.createWindowContext(session.getDisplay(),
+ TYPE_ACCESSIBILITY_OVERLAY, null /* options */);
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(
+ () -> context.getSystemService(WindowManager.class)
+ .addView(new View(context), mParams)
+ );
+ }
+ }
+
+ @Test(expected = WindowManager.BadTokenException.class)
+ public void testAddViewWithA11yServiceDerivedWindowContextWithDifferentType()
+ throws Exception {
+ try (VirtualDisplaySession session = new VirtualDisplaySession()) {
+ final Context context = mService.createWindowContext(session.getDisplay(),
+ TYPE_APPLICATION_OVERLAY, null /* options */);
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(
+ () -> context.getSystemService(WindowManager.class)
+ .addView(new View(context), mParams)
+ );
+ }
+ }
+
+
+ private static class VirtualDisplaySession implements AutoCloseable {
+ private final VirtualDisplay mVirtualDisplay;
+
+ VirtualDisplaySession() {
+ final DisplayManager displayManager = ApplicationProvider.getApplicationContext()
+ .getSystemService(DisplayManager.class);
+ final int width = 800;
+ final int height = 480;
+ final int density = 160;
+ ImageReader reader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888,
+ 2 /* maxImages */);
+ mVirtualDisplay = displayManager.createVirtualDisplay(
+ TAG, width, height, density, reader.getSurface(),
+ VIRTUAL_DISPLAY_FLAG_PUBLIC | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
+ }
+
+ private Display getDisplay() {
+ return mVirtualDisplay.getDisplay();
+ }
+
+ @Override
+ public void close() throws Exception {
+ mVirtualDisplay.release();
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java b/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java
index 49b720c..cf7e5c66 100644
--- a/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java
+++ b/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java
@@ -15,19 +15,18 @@
*/
package android.content.pm;
-import static android.content.pm.PackageParser.SigningDetails.CertCapabilities.AUTH;
-import static android.content.pm.PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA;
-import static android.content.pm.PackageParser.SigningDetails.CertCapabilities.PERMISSION;
-import static android.content.pm.PackageParser.SigningDetails.CertCapabilities.ROLLBACK;
-import static android.content.pm.PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID;
-import static android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3;
+import static android.content.pm.SigningDetails.CertCapabilities.AUTH;
+import static android.content.pm.SigningDetails.CertCapabilities.INSTALLED_DATA;
+import static android.content.pm.SigningDetails.CertCapabilities.PERMISSION;
+import static android.content.pm.SigningDetails.CertCapabilities.ROLLBACK;
+import static android.content.pm.SigningDetails.CertCapabilities.SHARED_USER_ID;
+import static android.content.pm.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import android.content.pm.PackageParser.SigningDetails;
import android.util.ArraySet;
import android.util.PackageUtils;
@@ -990,11 +989,11 @@
private void assertSigningDetailsContainsLineage(SigningDetails details,
String... pastSigners) {
// This method should only be invoked for results that contain a single signer.
- assertEquals(1, details.signatures.length);
- assertTrue(details.signatures[0].toCharsString().equalsIgnoreCase(
+ assertEquals(1, details.getSignatures().length);
+ assertTrue(details.getSignatures()[0].toCharsString().equalsIgnoreCase(
pastSigners[pastSigners.length - 1]));
Set<String> signatures = new ArraySet<>(pastSigners);
- for (Signature pastSignature : details.pastSigningCertificates) {
+ for (Signature pastSignature : details.getPastSigningCertificates()) {
assertTrue(signatures.remove(pastSignature.toCharsString()));
}
assertEquals(0, signatures.size());
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
index 46c96c9..2c8c385 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
@@ -21,6 +21,7 @@
import static junit.framework.Assert.assertTrue;
import android.os.Parcel;
+import android.view.Display;
import androidx.test.runner.AndroidJUnit4;
@@ -41,14 +42,14 @@
// and assertAccessibilityEventCleared
/** The number of properties of the {@link AccessibilityEvent} class. */
- private static final int A11Y_EVENT_NON_STATIC_FIELD_COUNT = 33;
+ private static final int A11Y_EVENT_NON_STATIC_FIELD_COUNT = 34;
// The number of fields tested in the corresponding CTS AccessibilityRecordTest:
// assertAccessibilityRecordCleared, fullyPopulateAccessibilityRecord,
// and assertEqualAccessibilityRecord
/** The number of properties of the {@link AccessibilityRecord} class. */
- private static final int A11Y_RECORD_NON_STATIC_FIELD_COUNT = 24;
+ private static final int A11Y_RECORD_NON_STATIC_FIELD_COUNT = 25;
@Test
public void testImportantForAccessibiity_getSetWorkAcrossParceling() {
@@ -69,6 +70,14 @@
}
@Test
+ public void testSourceDisplayId_getSetWorkAcrossParceling() {
+ final int sourceDisplayId = Display.DEFAULT_DISPLAY;
+ AccessibilityEvent event = AccessibilityEvent.obtain();
+ event.setDisplayId(sourceDisplayId);
+ assertEquals(sourceDisplayId, copyEventViaParcel(event).getDisplayId());
+ }
+
+ @Test
public void testWindowChanges_getSetWorkAcrossParceling() {
final int windowChanges = AccessibilityEvent.WINDOWS_CHANGE_TITLE
| AccessibilityEvent.WINDOWS_CHANGE_ACTIVE
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
index 8643a37..b71d814 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
@@ -166,4 +166,7 @@
public void logTrace(long timestamp, String where, String callingParams, int processId,
long threadId, int callingUid, Bundle callingStack) {}
+
+ public void logTrace(long timestamp, String where, long loggingTypes, String callingParams,
+ int processId, long threadId, int callingUid, Bundle serializedCallingStackInBundle) {}
}
diff --git a/core/tests/coretests/src/android/widget/ProgressBarTest.java b/core/tests/coretests/src/android/widget/ProgressBarTest.java
new file mode 100644
index 0000000..5b92471
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/ProgressBarTest.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Instrumentation;
+import android.platform.test.annotations.Presubmit;
+import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class ProgressBarTest {
+ private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ private ProgressBar mBar;
+ private AccessibilityNodeInfo mInfo;
+
+ @Before
+ public void setUp() throws Exception {
+ // enable accessibility
+ mInstrumentation.getUiAutomation();
+ // create ProgressBar on main thread and call setProgress on main thread
+ mInstrumentation.runOnMainSync(() ->
+ mBar = new ProgressBar(
+ InstrumentationRegistry.getInstrumentation().getContext(),
+ null,
+ com.android.internal.R.attr.progressBarStyleHorizontal
+ )
+ );
+ mInfo = AccessibilityNodeInfo.obtain();
+ }
+
+ @After
+ public void tearDown() {
+ mInfo.recycle();
+ }
+
+ @Test
+ public void testStateDescription_determinateProgressBar_default() {
+ mBar.setIndeterminate(false);
+ assertFalse(mBar.isIndeterminate());
+
+ mInstrumentation.runOnMainSync(() -> mBar.setProgress(50));
+
+ mBar.onInitializeAccessibilityNodeInfo(mInfo);
+ assertEquals("50%", mInfo.getStateDescription().toString());
+ }
+
+ @Test
+ public void testStateDescription_determinateProgressBar_custom_viewApi() {
+ mBar.setIndeterminate(false);
+ assertFalse(mBar.isIndeterminate());
+ // A workaround for the not-attached ProgressBar.
+ mBar.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ info.setStateDescription(host.getStateDescription());
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ }
+ });
+
+ mBar.setStateDescription("custom state");
+ mInstrumentation.runOnMainSync(() -> mBar.setProgress(50));
+
+ assertEquals("custom state", mBar.getStateDescription().toString());
+ mBar.onInitializeAccessibilityNodeInfo(mInfo);
+ assertEquals("custom state", mInfo.getStateDescription().toString());
+
+ mBar.setStateDescription(null);
+
+ assertNull(mBar.getStateDescription());
+ mInfo.recycle();
+ mInfo = AccessibilityNodeInfo.obtain();
+ mBar.onInitializeAccessibilityNodeInfo(mInfo);
+ assertEquals("50%", mInfo.getStateDescription().toString());
+ }
+
+ @Test
+ public void testStateDescription_determinateProgressBar_custom_accessibilityNodeInfoApi() {
+ mBar.setIndeterminate(false);
+ assertFalse(mBar.isIndeterminate());
+ mBar.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ info.setStateDescription("custom state");
+ }
+ });
+
+ mInstrumentation.runOnMainSync(() -> mBar.setProgress(50));
+
+ mBar.onInitializeAccessibilityNodeInfo(mInfo);
+ assertEquals("custom state", mInfo.getStateDescription().toString());
+ }
+
+ @Test
+ public void testStateDescription_indeterminateProgressBar_default() {
+ mBar.setIndeterminate(true);
+ assertTrue(mBar.isIndeterminate());
+
+ // call setMax to invoke call to ProgressBar#onProgressRefresh()
+ mInstrumentation.runOnMainSync(() -> mBar.setMax(200));
+
+ mBar.onInitializeAccessibilityNodeInfo(mInfo);
+ assertEquals("in progress", mInfo.getStateDescription().toString());
+ }
+
+ @Test
+ public void testStateDescription_indeterminateProgressBar_custom_viewApi() {
+ mBar.setIndeterminate(true);
+ assertTrue(mBar.isIndeterminate());
+ // A workaround for the not-attached ProgressBar.
+ mBar.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ info.setStateDescription(host.getStateDescription());
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ }
+ });
+
+ mBar.setStateDescription("custom state");
+ // call setMax to invoke call to ProgressBar#onProgressRefresh()
+ mInstrumentation.runOnMainSync(() -> mBar.setMax(200));
+
+ assertEquals("custom state", mBar.getStateDescription().toString());
+ mBar.onInitializeAccessibilityNodeInfo(mInfo);
+ assertEquals("custom state", mInfo.getStateDescription().toString());
+
+ mBar.setStateDescription(null);
+
+ assertNull(mBar.getStateDescription());
+ mInfo.recycle();
+ mInfo = AccessibilityNodeInfo.obtain();
+ mBar.onInitializeAccessibilityNodeInfo(mInfo);
+ assertEquals("in progress", mInfo.getStateDescription().toString());
+ }
+
+ @Test
+ public void testStateDescription_indeterminateProgressBar_custom_accessibilityNodeInfoApi() {
+ mBar.setIndeterminate(true);
+ assertTrue(mBar.isIndeterminate());
+ mBar.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ info.setStateDescription("custom state");
+ }
+ });
+
+ // call setMax to invoke call to ProgressBar#onProgressRefresh()
+ mInstrumentation.runOnMainSync(() -> mBar.setMax(200));
+
+ mBar.onInitializeAccessibilityNodeInfo(mInfo);
+ assertEquals("custom state", mInfo.getStateDescription().toString());
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityUtilsTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityUtilsTest.java
new file mode 100644
index 0000000..045b3a2
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityUtilsTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.accessibility;
+
+import static junit.framework.Assert.assertEquals;
+
+import android.text.ParcelableSpan;
+import android.text.SpannableString;
+import android.text.style.LocaleSpan;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.accessibility.util.AccessibilityUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Locale;
+
+/**
+ * Unit tests for AccessibilityUtils.
+ */
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityUtilsTest {
+ @Test
+ public void textOrSpanChanged_stringChange_returnTextChange() {
+ final CharSequence beforeText = "a";
+
+ final CharSequence afterText = "b";
+
+ @AccessibilityUtils.A11yTextChangeType int type = AccessibilityUtils.textOrSpanChanged(
+ beforeText, afterText);
+ assertEquals(AccessibilityUtils.TEXT, type);
+ }
+
+ @Test
+ public void textOrSpanChanged_stringNotChange_returnNoneChange() {
+ final CharSequence beforeText = "a";
+
+ final CharSequence afterText = "a";
+
+ @AccessibilityUtils.A11yTextChangeType int type = AccessibilityUtils.textOrSpanChanged(
+ beforeText, afterText);
+ assertEquals(AccessibilityUtils.NONE, type);
+ }
+
+ @Test
+ public void textOrSpanChanged_nonSpanToNonParcelableSpan_returnNoneChange() {
+ final Object nonParcelableSpan = new Object();
+ final CharSequence beforeText = new SpannableString("a");
+
+ final SpannableString afterText = new SpannableString("a");
+ afterText.setSpan(nonParcelableSpan, 0, 1, 0);
+
+ @AccessibilityUtils.A11yTextChangeType int type = AccessibilityUtils.textOrSpanChanged(
+ beforeText, afterText);
+ assertEquals(AccessibilityUtils.NONE, type);
+ }
+
+ @Test
+ public void textOrSpanChanged_nonSpanToParcelableSpan_returnParcelableSpanChange() {
+ final ParcelableSpan parcelableSpan = new LocaleSpan(Locale.ENGLISH);
+ final CharSequence beforeText = new SpannableString("a");
+
+ final SpannableString afterText = new SpannableString("a");
+ afterText.setSpan(parcelableSpan, 0, 1, 0);
+
+ @AccessibilityUtils.A11yTextChangeType int type = AccessibilityUtils.textOrSpanChanged(
+ beforeText, afterText);
+ assertEquals(AccessibilityUtils.PARCELABLE_SPAN, type);
+ }
+
+ @Test
+ public void textOrSpanChanged_nonParcelableSpanToParcelableSpan_returnParcelableSpanChange() {
+ final Object nonParcelableSpan = new Object();
+ final ParcelableSpan parcelableSpan = new LocaleSpan(Locale.ENGLISH);
+ final SpannableString beforeText = new SpannableString("a");
+ beforeText.setSpan(nonParcelableSpan, 0, 1, 0);
+
+ SpannableString afterText = new SpannableString("a");
+ afterText.setSpan(parcelableSpan, 0, 1, 0);
+
+ @AccessibilityUtils.A11yTextChangeType int type = AccessibilityUtils.textOrSpanChanged(
+ beforeText, afterText);
+ assertEquals(AccessibilityUtils.PARCELABLE_SPAN, type);
+ }
+
+ @Test
+ public void textOrSpanChanged_nonParcelableSpanChange_returnNoneChange() {
+ final Object nonParcelableSpan = new Object();
+ final SpannableString beforeText = new SpannableString("a");
+ beforeText.setSpan(nonParcelableSpan, 0, 1, 0);
+
+ final SpannableString afterText = new SpannableString("a");
+ afterText.setSpan(nonParcelableSpan, 1, 1, 0);
+
+ @AccessibilityUtils.A11yTextChangeType int type = AccessibilityUtils.textOrSpanChanged(
+ beforeText, afterText);
+ assertEquals(AccessibilityUtils.NONE, type);
+ }
+
+ @Test
+ public void textOrSpanChanged_parcelableSpanChange_returnParcelableSpanChange() {
+ final ParcelableSpan parcelableSpan = new LocaleSpan(Locale.ENGLISH);
+ final SpannableString beforeText = new SpannableString("a");
+ beforeText.setSpan(parcelableSpan, 0, 1, 0);
+
+ final SpannableString afterText = new SpannableString("a");
+ afterText.setSpan(parcelableSpan, 1, 1, 0);
+
+ @AccessibilityUtils.A11yTextChangeType int type = AccessibilityUtils.textOrSpanChanged(
+ beforeText, afterText);
+ assertEquals(AccessibilityUtils.PARCELABLE_SPAN, type);
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index 46e2772..90a9572 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -44,6 +44,7 @@
BatteryStatsUidTest.class,
BatteryUsageStatsProviderTest.class,
BatteryUsageStatsTest.class,
+ BatteryUsageStatsStoreTest.class,
BatteryStatsUserLifecycleTests.class,
BluetoothPowerCalculatorTest.class,
BstatsCpuTimesValidationTest.class,
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
index d83645d..cbd67c8 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
@@ -20,10 +20,14 @@
import android.app.ActivityManager;
import android.content.Context;
+import android.os.BatteryConsumer;
import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
import android.os.Parcel;
import android.os.Process;
import android.os.UidBatteryConsumer;
@@ -36,6 +40,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.File;
import java.util.List;
@SmallTest
@@ -45,7 +50,8 @@
private static final long MINUTE_IN_MS = 60 * 1000;
@Rule
- public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule(12345);
+ public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule(12345)
+ .setAveragePower(PowerProfile.POWER_FLASHLIGHT, 360.0);
@Test
public void test_getBatteryUsageStats() {
@@ -187,4 +193,84 @@
mStatsRule.setTime(11500, 0);
assertThat(provider.shouldUpdateStats(queries, 10000)).isTrue();
}
+
+ @Test
+ public void testAggregateBatteryStats() {
+ Context context = InstrumentationRegistry.getContext();
+ BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+ mStatsRule.setCurrentTime(5 * MINUTE_IN_MS);
+ batteryStats.resetAllStatsCmdLocked();
+
+ BatteryUsageStatsStore batteryUsageStatsStore = new BatteryUsageStatsStore(context,
+ batteryStats, new File(context.getCacheDir(), "BatteryUsageStatsProviderTest"),
+ new TestHandler(), Integer.MAX_VALUE);
+
+ BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context,
+ batteryStats, batteryUsageStatsStore);
+
+ batteryStats.noteFlashlightOnLocked(APP_UID,
+ 10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS);
+ batteryStats.noteFlashlightOffLocked(APP_UID,
+ 20 * MINUTE_IN_MS, 20 * MINUTE_IN_MS);
+ mStatsRule.setCurrentTime(25 * MINUTE_IN_MS);
+ batteryStats.resetAllStatsCmdLocked();
+
+ batteryStats.noteFlashlightOnLocked(APP_UID,
+ 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS);
+ batteryStats.noteFlashlightOffLocked(APP_UID,
+ 50 * MINUTE_IN_MS, 50 * MINUTE_IN_MS);
+ mStatsRule.setCurrentTime(55 * MINUTE_IN_MS);
+ batteryStats.resetAllStatsCmdLocked();
+
+ // This section should be ignored because the timestamp is out or range
+ batteryStats.noteFlashlightOnLocked(APP_UID,
+ 60 * MINUTE_IN_MS, 60 * MINUTE_IN_MS);
+ batteryStats.noteFlashlightOffLocked(APP_UID,
+ 70 * MINUTE_IN_MS, 70 * MINUTE_IN_MS);
+ mStatsRule.setCurrentTime(75 * MINUTE_IN_MS);
+ batteryStats.resetAllStatsCmdLocked();
+
+ // This section should be ignored because it represents the current stats session
+ batteryStats.noteFlashlightOnLocked(APP_UID,
+ 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS);
+ batteryStats.noteFlashlightOffLocked(APP_UID,
+ 90 * MINUTE_IN_MS, 90 * MINUTE_IN_MS);
+ mStatsRule.setCurrentTime(95 * MINUTE_IN_MS);
+
+ // Include the first and the second snapshot, but not the third or current
+ BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder()
+ .aggregateSnapshots(20 * MINUTE_IN_MS, 60 * MINUTE_IN_MS)
+ .build();
+ final BatteryUsageStats stats = provider.getBatteryUsageStats(query);
+
+ assertThat(stats.getStatsStartTimestamp()).isEqualTo(5 * MINUTE_IN_MS);
+ assertThat(stats.getStatsEndTimestamp()).isEqualTo(55 * MINUTE_IN_MS);
+ assertThat(stats.getAggregateBatteryConsumer(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
+ .getConsumedPower(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT))
+ .isWithin(0.0001)
+ .of(180.0); // 360 mA * 0.5 hours
+ assertThat(stats.getAggregateBatteryConsumer(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
+ .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT))
+ .isEqualTo((10 + 20) * MINUTE_IN_MS);
+ final UidBatteryConsumer uidBatteryConsumer = stats.getUidBatteryConsumers().stream()
+ .filter(uid -> uid.getUid() == APP_UID).findFirst().get();
+ assertThat(uidBatteryConsumer
+ .getConsumedPower(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT))
+ .isWithin(0.1)
+ .of(180.0);
+ }
+
+ private static class TestHandler extends Handler {
+ TestHandler() {
+ super(Looper.getMainLooper());
+ }
+
+ @Override
+ public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
+ msg.getCallback().run();
+ return true;
+ }
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsStoreTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsStoreTest.java
new file mode 100644
index 0000000..141a9fa
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsStoreTest.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
+import android.os.BatteryManager;
+import android.os.BatteryUsageStats;
+import android.os.BatteryUsageStatsQuery;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+@RunWith(AndroidJUnit4.class)
+public class BatteryUsageStatsStoreTest {
+ private static final long MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES = 2 * 1024;
+
+ private final MockClocks mMockClocks = new MockClocks();
+ private MockBatteryStatsImpl mBatteryStats;
+ private BatteryUsageStatsStore mBatteryUsageStatsStore;
+ private BatteryUsageStatsProvider mBatteryUsageStatsProvider;
+ private File mStoreDirectory;
+
+ @Before
+ public void setup() {
+ mMockClocks.currentTime = 123;
+ mBatteryStats = new MockBatteryStatsImpl(mMockClocks);
+ mBatteryStats.setNoAutoReset(true);
+ mBatteryStats.setPowerProfile(mock(PowerProfile.class));
+
+ Context context = InstrumentationRegistry.getContext();
+
+ mStoreDirectory = new File(context.getCacheDir(), "BatteryUsageStatsStoreTest");
+ clearDirectory(mStoreDirectory);
+
+ mBatteryUsageStatsStore = new BatteryUsageStatsStore(context, mBatteryStats,
+ mStoreDirectory, new TestHandler(), MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES);
+
+ mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mBatteryStats);
+ }
+
+ @Test
+ public void testStoreSnapshot() {
+ mMockClocks.currentTime = 1_600_000;
+
+ prepareBatteryStats();
+ mBatteryStats.resetAllStatsCmdLocked();
+
+ final long[] timestamps = mBatteryUsageStatsStore.listBatteryUsageStatsTimestamps();
+ assertThat(timestamps).hasLength(1);
+ assertThat(timestamps[0]).isEqualTo(1_600_000);
+
+ final BatteryUsageStats batteryUsageStats = mBatteryUsageStatsStore.loadBatteryUsageStats(
+ 1_600_000);
+ assertThat(batteryUsageStats.getStatsStartTimestamp()).isEqualTo(123);
+ assertThat(batteryUsageStats.getStatsEndTimestamp()).isEqualTo(1_600_000);
+ assertThat(batteryUsageStats.getBatteryCapacity()).isEqualTo(4000);
+ assertThat(batteryUsageStats.getDischargePercentage()).isEqualTo(5);
+ assertThat(batteryUsageStats.getAggregateBatteryConsumer(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE).getConsumedPower())
+ .isEqualTo(600); // (3_600_000 - 3_000_000) / 1000
+ }
+
+ @Test
+ public void testGarbageCollectOldSnapshots() throws Exception {
+ prepareBatteryStats();
+
+ mMockClocks.realtime = 10_000_000;
+ mMockClocks.uptime = 10_000_000;
+ mMockClocks.currentTime = 10_000_000;
+
+ final int snapshotFileSize = getSnapshotFileSize();
+ final int numberOfSnapshots =
+ (int) (MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES / snapshotFileSize);
+ for (int i = 0; i < numberOfSnapshots + 2; i++) {
+ mBatteryStats.resetAllStatsCmdLocked();
+
+ mMockClocks.realtime += 10_000_000;
+ mMockClocks.uptime += 10_000_000;
+ mMockClocks.currentTime += 10_000_000;
+ prepareBatteryStats();
+ }
+
+ final long[] timestamps = mBatteryUsageStatsStore.listBatteryUsageStatsTimestamps();
+ Arrays.sort(timestamps);
+ assertThat(timestamps).hasLength(numberOfSnapshots);
+ // Two snapshots (10_000_000 and 20_000_000) should have been discarded
+ assertThat(timestamps[0]).isEqualTo(30_000_000);
+ assertThat(getDirectorySize(mStoreDirectory))
+ .isAtMost(MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES);
+ }
+
+ @Test
+ public void testSavingStatsdAtomPullTimestamp() {
+ mBatteryUsageStatsStore.setLastBatteryUsageStatsBeforeResetAtomPullTimestamp(1234);
+ assertThat(mBatteryUsageStatsStore.getLastBatteryUsageStatsBeforeResetAtomPullTimestamp())
+ .isEqualTo(1234);
+ mBatteryUsageStatsStore.setLastBatteryUsageStatsBeforeResetAtomPullTimestamp(5478);
+ assertThat(mBatteryUsageStatsStore.getLastBatteryUsageStatsBeforeResetAtomPullTimestamp())
+ .isEqualTo(5478);
+ }
+
+ private void prepareBatteryStats() {
+ mBatteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 100,
+ /* plugType */ 0, 90, 72, 3700, 3_600_000, 4_000_000, 0,
+ mMockClocks.realtime, mMockClocks.uptime, mMockClocks.currentTime);
+ mBatteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 100,
+ /* plugType */ 0, 85, 72, 3700, 3_000_000, 4_000_000, 0,
+ mMockClocks.realtime + 500_000, mMockClocks.uptime + 500_000,
+ mMockClocks.currentTime + 500_000);
+ }
+
+ private void clearDirectory(File dir) {
+ if (dir.exists()) {
+ for (File child : dir.listFiles()) {
+ if (child.isDirectory()) {
+ clearDirectory(child);
+ }
+ child.delete();
+ }
+ }
+ }
+
+ private long getDirectorySize(File dir) {
+ long size = 0;
+ if (dir.exists()) {
+ for (File child : dir.listFiles()) {
+ if (child.isDirectory()) {
+ size += getDirectorySize(child);
+ } else {
+ size += child.length();
+ }
+ }
+ }
+ return size;
+ }
+
+ private int getSnapshotFileSize() throws IOException {
+ BatteryUsageStats stats = mBatteryUsageStatsProvider.getBatteryUsageStats(
+ new BatteryUsageStatsQuery.Builder()
+ .setMaxStatsAgeMs(0)
+ .includePowerModels()
+ .build());
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ TypedXmlSerializer serializer = Xml.newBinarySerializer();
+ serializer.setOutput(out, StandardCharsets.UTF_8.name());
+ serializer.startDocument(null, true);
+ stats.writeXml(serializer);
+ serializer.endDocument();
+ return out.toByteArray().length;
+ }
+
+ private static class TestHandler extends Handler {
+ TestHandler() {
+ super(Looper.getMainLooper());
+ }
+
+ @Override
+ public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
+ msg.getCallback().run();
+ return true;
+ }
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
index 380b4ae..3e620c2 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
@@ -16,8 +16,13 @@
package com.android.internal.os;
+import static android.os.BatteryConsumer.POWER_MODEL_MEASURED_ENERGY;
+import static android.os.BatteryConsumer.POWER_MODEL_POWER_PROFILE;
+import static android.os.BatteryConsumer.POWER_MODEL_UNDEFINED;
+
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail;
import android.os.BatteryConsumer;
@@ -25,6 +30,9 @@
import android.os.Parcel;
import android.os.UidBatteryConsumer;
import android.os.UserBatteryConsumer;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -32,8 +40,11 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -42,15 +53,19 @@
@RunWith(AndroidJUnit4.class)
public class BatteryUsageStatsTest {
+ private static final int USER_ID = 42;
+ private static final int APP_UID1 = 271;
+ private static final int APP_UID2 = 314;
+
@Test
public void testBuilder() {
- BatteryUsageStats batteryUsageStats = buildBatteryUsageStats().build();
- validateBatteryUsageStats(batteryUsageStats);
+ BatteryUsageStats batteryUsageStats = buildBatteryUsageStats1(true).build();
+ assertBatteryUsageStats1(batteryUsageStats, true);
}
@Test
public void testParcelability() {
- final BatteryUsageStats outBatteryUsageStats = buildBatteryUsageStats().build();
+ final BatteryUsageStats outBatteryUsageStats = buildBatteryUsageStats1(true).build();
final Parcel outParcel = Parcel.obtain();
outParcel.writeParcelable(outBatteryUsageStats, 0);
final byte[] bytes = outParcel.marshall();
@@ -62,20 +77,20 @@
final BatteryUsageStats inBatteryUsageStats =
inParcel.readParcelable(getClass().getClassLoader());
assertThat(inBatteryUsageStats).isNotNull();
- validateBatteryUsageStats(inBatteryUsageStats);
+ assertBatteryUsageStats1(inBatteryUsageStats, true);
}
@Test
public void testDefaultSessionDuration() {
final BatteryUsageStats stats =
- buildBatteryUsageStats().setStatsDuration(10000).build();
+ buildBatteryUsageStats1(true).setStatsDuration(10000).build();
assertThat(stats.getStatsDuration()).isEqualTo(10000);
}
@Test
public void testDump() {
- final BatteryUsageStats stats = buildBatteryUsageStats().build();
+ final BatteryUsageStats stats = buildBatteryUsageStats1(true).build();
final StringWriter out = new StringWriter();
try (PrintWriter pw = new PrintWriter(out)) {
stats.dump(pw, " ");
@@ -87,7 +102,7 @@
assertThat(dump).contains("actual drain: 1000-2000");
assertThat(dump).contains("cpu: 20100 apps: 10100 duration: 20s 300ms");
assertThat(dump).contains("FOO: 20200 apps: 10200 duration: 20s 400ms");
- assertThat(dump).contains("UID 2000: 1200 ( screen=300 cpu=400 FOO=500 )");
+ assertThat(dump).contains("UID 271: 1200 ( screen=300 cpu=400 FOO=500 )");
assertThat(dump).contains("User 42: 30.0 ( cpu=10.0 FOO=20.0 )");
}
@@ -101,154 +116,297 @@
assertThat(allNames).hasSize(BatteryConsumer.POWER_COMPONENT_COUNT);
}
- private BatteryUsageStats.Builder buildBatteryUsageStats() {
- final MockClocks clocks = new MockClocks();
- final MockBatteryStatsImpl batteryStats = new MockBatteryStatsImpl(clocks);
- final BatteryStatsImpl.Uid batteryStatsUid = batteryStats.getUidStatsLocked(2000);
+ @Test
+ public void testAdd() {
+ final BatteryUsageStats stats1 = buildBatteryUsageStats1(false).build();
+ final BatteryUsageStats stats2 = buildBatteryUsageStats2(new String[] {"FOO"}).build();
- final BatteryUsageStats.Builder builder =
- new BatteryUsageStats.Builder(new String[]{"FOO"}, true)
- .setBatteryCapacity(4000)
- .setDischargePercentage(20)
- .setDischargedPowerRange(1000, 2000)
- .setStatsStartTimestamp(1000)
- .setStatsEndTimestamp(3000);
- builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid)
- .setPackageWithHighestDrain("foo")
- .setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, 1000)
- .setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND, 2000)
- .setConsumedPower(
- BatteryConsumer.POWER_COMPONENT_SCREEN, 300)
- .setConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU, 400)
- .setConsumedPowerForCustomComponent(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 500)
- .setUsageDurationMillis(
- BatteryConsumer.POWER_COMPONENT_CPU, 600)
- .setUsageDurationForCustomComponentMillis(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 800);
+ final BatteryUsageStats sum =
+ new BatteryUsageStats.Builder(new String[] {"FOO"}, true)
+ .add(stats1)
+ .add(stats2)
+ .build();
- builder.getAggregateBatteryConsumerBuilder(
- BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
- .setConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU, 10100)
- .setConsumedPowerForCustomComponent(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200)
- .setUsageDurationMillis(
- BatteryConsumer.POWER_COMPONENT_CPU, 10300)
- .setUsageDurationForCustomComponentMillis(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10400);
-
- builder.getAggregateBatteryConsumerBuilder(
- BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
- .setConsumedPower(30000)
- .setConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU, 20100)
- .setConsumedPowerForCustomComponent(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 20200)
- .setUsageDurationMillis(
- BatteryConsumer.POWER_COMPONENT_CPU, 20300)
- .setUsageDurationForCustomComponentMillis(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 20400);
-
- builder.getOrCreateUserBatteryConsumerBuilder(42)
- .setConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU, 10)
- .setConsumedPowerForCustomComponent(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 20)
- .setUsageDurationMillis(
- BatteryConsumer.POWER_COMPONENT_CPU, 30)
- .setUsageDurationForCustomComponentMillis(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 40);
-
- return builder;
- }
-
- public void validateBatteryUsageStats(BatteryUsageStats batteryUsageStats) {
- assertThat(batteryUsageStats.getConsumedPower()).isEqualTo(30000);
- assertThat(batteryUsageStats.getBatteryCapacity()).isEqualTo(4000);
- assertThat(batteryUsageStats.getDischargePercentage()).isEqualTo(20);
- assertThat(batteryUsageStats.getDischargedPowerRange().getLower()).isEqualTo(1000);
- assertThat(batteryUsageStats.getDischargedPowerRange().getUpper()).isEqualTo(2000);
- assertThat(batteryUsageStats.getStatsStartTimestamp()).isEqualTo(1000);
- assertThat(batteryUsageStats.getStatsEndTimestamp()).isEqualTo(3000);
- assertThat(batteryUsageStats.getStatsDuration()).isEqualTo(2000);
+ assertBatteryUsageStats(sum, 42345, 50, 2234, 4345, 1000, 5000, 5000);
final List<UidBatteryConsumer> uidBatteryConsumers =
- batteryUsageStats.getUidBatteryConsumers();
+ sum.getUidBatteryConsumers();
for (UidBatteryConsumer uidBatteryConsumer : uidBatteryConsumers) {
- if (uidBatteryConsumer.getUid() == 2000) {
- assertThat(uidBatteryConsumer.getPackageWithHighestDrain()).isEqualTo("foo");
- assertThat(uidBatteryConsumer.getTimeInStateMs(
- UidBatteryConsumer.STATE_FOREGROUND)).isEqualTo(1000);
- assertThat(uidBatteryConsumer.getTimeInStateMs(
- UidBatteryConsumer.STATE_BACKGROUND)).isEqualTo(2000);
- assertThat(uidBatteryConsumer.getConsumedPower(
- BatteryConsumer.POWER_COMPONENT_SCREEN)).isEqualTo(300);
- assertThat(uidBatteryConsumer.getConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(400);
- assertThat(uidBatteryConsumer.getConsumedPowerForCustomComponent(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(500);
- assertThat(uidBatteryConsumer.getUsageDurationMillis(
- BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(600);
- assertThat(uidBatteryConsumer.getUsageDurationForCustomComponentMillis(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(800);
- assertThat(uidBatteryConsumer.getConsumedPower()).isEqualTo(1200);
- assertThat(uidBatteryConsumer.getCustomPowerComponentCount()).isEqualTo(1);
- assertThat(uidBatteryConsumer.getCustomPowerComponentName(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo("FOO");
+ if (uidBatteryConsumer.getUid() == APP_UID1) {
+ assertUidBatteryConsumer(uidBatteryConsumer, 2124, null,
+ 5321, 7432, 423, POWER_MODEL_POWER_PROFILE, 745, POWER_MODEL_UNDEFINED,
+ 956, 1167, 1478);
+ } else if (uidBatteryConsumer.getUid() == APP_UID2) {
+ assertUidBatteryConsumer(uidBatteryConsumer, 1332, "bar",
+ 1111, 2222, 333, POWER_MODEL_POWER_PROFILE, 444, POWER_MODEL_POWER_PROFILE,
+ 555, 666, 777);
} else {
fail("Unexpected UID " + uidBatteryConsumer.getUid());
}
}
- final BatteryConsumer appsBatteryConsumer = batteryUsageStats.getAggregateBatteryConsumer(
- BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS);
- assertThat(appsBatteryConsumer.getConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(10100);
- assertThat(appsBatteryConsumer.getConsumedPowerForCustomComponent(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(10200);
- assertThat(appsBatteryConsumer.getUsageDurationMillis(
- BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(10300);
- assertThat(appsBatteryConsumer.getUsageDurationForCustomComponentMillis(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(10400);
- assertThat(appsBatteryConsumer.getCustomPowerComponentCount()).isEqualTo(1);
- assertThat(appsBatteryConsumer.getCustomPowerComponentName(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo("FOO");
+ assertAggregateBatteryConsumer(sum,
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS,
+ 20223, 20434, 20645, 20856);
- final BatteryConsumer deviceBatteryConsumer = batteryUsageStats.getAggregateBatteryConsumer(
- BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
- assertThat(deviceBatteryConsumer.getConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(20100);
- assertThat(deviceBatteryConsumer.getConsumedPowerForCustomComponent(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(20200);
- assertThat(deviceBatteryConsumer.getUsageDurationMillis(
- BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(20300);
- assertThat(deviceBatteryConsumer.getUsageDurationForCustomComponentMillis(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(20400);
- assertThat(deviceBatteryConsumer.getCustomPowerComponentCount()).isEqualTo(1);
- assertThat(deviceBatteryConsumer.getCustomPowerComponentName(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo("FOO");
+ assertAggregateBatteryConsumer(sum,
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE,
+ 40211, 40422, 40633, 40844);
+ }
+
+ @Test
+ public void testAdd_customComponentMismatch() {
+ final BatteryUsageStats.Builder builder =
+ new BatteryUsageStats.Builder(new String[] {"FOO"}, true);
+ final BatteryUsageStats stats = buildBatteryUsageStats2(new String[] {"BAR"}).build();
+
+ assertThrows(IllegalArgumentException.class, () -> builder.add(stats));
+ }
+
+ @Test
+ public void testXml() throws Exception {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ TypedXmlSerializer serializer = Xml.newBinarySerializer();
+ serializer.setOutput(out, StandardCharsets.UTF_8.name());
+ serializer.startDocument(null, true);
+ final BatteryUsageStats stats = buildBatteryUsageStats1(true).build();
+ stats.writeXml(serializer);
+ serializer.endDocument();
+
+ ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+ TypedXmlPullParser parser = Xml.newBinaryPullParser();
+ parser.setInput(in, StandardCharsets.UTF_8.name());
+ final BatteryUsageStats fromXml = BatteryUsageStats.createFromXml(parser);
+
+ assertBatteryUsageStats1(fromXml, true);
+ }
+
+ private BatteryUsageStats.Builder buildBatteryUsageStats1(boolean includeUserBatteryConsumer) {
+ final MockClocks clocks = new MockClocks();
+ final MockBatteryStatsImpl batteryStats = new MockBatteryStatsImpl(clocks);
+
+ final BatteryUsageStats.Builder builder =
+ new BatteryUsageStats.Builder(new String[] {"FOO"}, true)
+ .setBatteryCapacity(4000)
+ .setDischargePercentage(20)
+ .setDischargedPowerRange(1000, 2000)
+ .setStatsStartTimestamp(1000)
+ .setStatsEndTimestamp(3000);
+
+ addUidBatteryConsumer(builder, batteryStats, APP_UID1, "foo",
+ 1000, 2000,
+ 300, POWER_MODEL_POWER_PROFILE, 400, POWER_MODEL_POWER_PROFILE, 500, 600, 800);
+
+ addAggregateBatteryConsumer(builder,
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS, 0,
+ 10100, 10200, 10300, 10400);
+
+ addAggregateBatteryConsumer(builder,
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE, 30000,
+ 20100, 20200, 20300, 20400);
+
+
+ if (includeUserBatteryConsumer) {
+ builder.getOrCreateUserBatteryConsumerBuilder(USER_ID)
+ .setConsumedPower(
+ BatteryConsumer.POWER_COMPONENT_CPU, 10)
+ .setConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 20)
+ .setUsageDurationMillis(
+ BatteryConsumer.POWER_COMPONENT_CPU, 30)
+ .setUsageDurationForCustomComponentMillis(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 40);
+ }
+ return builder;
+ }
+
+ private BatteryUsageStats.Builder buildBatteryUsageStats2(String[] customPowerComponentNames) {
+ final MockClocks clocks = new MockClocks();
+ final MockBatteryStatsImpl batteryStats = new MockBatteryStatsImpl(clocks);
+
+ final BatteryUsageStats.Builder builder =
+ new BatteryUsageStats.Builder(customPowerComponentNames, true)
+ .setDischargePercentage(30)
+ .setDischargedPowerRange(1234, 2345)
+ .setStatsStartTimestamp(2000)
+ .setStatsEndTimestamp(5000);
+
+ addUidBatteryConsumer(builder, batteryStats, APP_UID1, null,
+ 4321, 5432,
+ 123, POWER_MODEL_POWER_PROFILE, 345, POWER_MODEL_MEASURED_ENERGY, 456, 567, 678);
+
+ addUidBatteryConsumer(builder, batteryStats, APP_UID2, "bar",
+ 1111, 2222,
+ 333, POWER_MODEL_POWER_PROFILE, 444, POWER_MODEL_POWER_PROFILE, 555, 666, 777);
+
+ addAggregateBatteryConsumer(builder,
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS, 0,
+ 10123, 10234, 10345, 10456);
+
+ addAggregateBatteryConsumer(builder,
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE, 12345,
+ 20111, 20222, 20333, 20444);
+
+ return builder;
+ }
+
+ private void addUidBatteryConsumer(BatteryUsageStats.Builder builder,
+ MockBatteryStatsImpl batteryStats, int uid, String packageWithHighestDrain,
+ int timeInStateForeground, int timeInStateBackground, int screenPower,
+ int screenPowerModel, int cpuPower, int cpuPowerModel, int customComponentPower,
+ int cpuDuration, int customComponentDuration) {
+ final BatteryStatsImpl.Uid batteryStatsUid = batteryStats.getUidStatsLocked(uid);
+ builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid)
+ .setPackageWithHighestDrain(packageWithHighestDrain)
+ .setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, timeInStateForeground)
+ .setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND, timeInStateBackground)
+ .setConsumedPower(
+ BatteryConsumer.POWER_COMPONENT_SCREEN, screenPower, screenPowerModel)
+ .setConsumedPower(
+ BatteryConsumer.POWER_COMPONENT_CPU, cpuPower, cpuPowerModel)
+ .setConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, customComponentPower)
+ .setUsageDurationMillis(
+ BatteryConsumer.POWER_COMPONENT_CPU, cpuDuration)
+ .setUsageDurationForCustomComponentMillis(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, customComponentDuration);
+ }
+
+ private void addAggregateBatteryConsumer(BatteryUsageStats.Builder builder, int scope,
+ double consumedPower, int cpuPower, int customComponentPower, int cpuDuration,
+ int customComponentDuration) {
+ builder.getAggregateBatteryConsumerBuilder(scope)
+ .setConsumedPower(consumedPower)
+ .setConsumedPower(
+ BatteryConsumer.POWER_COMPONENT_CPU, cpuPower)
+ .setConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, customComponentPower)
+ .setUsageDurationMillis(
+ BatteryConsumer.POWER_COMPONENT_CPU, cpuDuration)
+ .setUsageDurationForCustomComponentMillis(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, customComponentDuration);
+ }
+
+ public void assertBatteryUsageStats1(BatteryUsageStats batteryUsageStats,
+ boolean includesUserBatteryConsumers) {
+ assertBatteryUsageStats(batteryUsageStats, 30000, 20, 1000, 2000, 1000, 3000, 2000);
+
+ final List<UidBatteryConsumer> uidBatteryConsumers =
+ batteryUsageStats.getUidBatteryConsumers();
+ assertThat(uidBatteryConsumers).hasSize(1);
+ for (UidBatteryConsumer uidBatteryConsumer : uidBatteryConsumers) {
+ if (uidBatteryConsumer.getUid() == APP_UID1) {
+ assertUidBatteryConsumer(uidBatteryConsumer, 1200, "foo",
+ 1000, 2000, 300, POWER_MODEL_POWER_PROFILE, 400, POWER_MODEL_POWER_PROFILE,
+ 500, 600, 800);
+ } else {
+ fail("Unexpected UID " + uidBatteryConsumer.getUid());
+ }
+ }
final List<UserBatteryConsumer> userBatteryConsumers =
batteryUsageStats.getUserBatteryConsumers();
- for (UserBatteryConsumer userBatteryConsumer : userBatteryConsumers) {
- if (userBatteryConsumer.getUserId() == 42) {
- assertThat(userBatteryConsumer.getConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(10);
- assertThat(userBatteryConsumer.getConsumedPowerForCustomComponent(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(20);
- assertThat(userBatteryConsumer.getUsageDurationMillis(
- BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(30);
- assertThat(userBatteryConsumer.getUsageDurationForCustomComponentMillis(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(40);
- assertThat(userBatteryConsumer.getConsumedPower()).isEqualTo(30);
- assertThat(userBatteryConsumer.getCustomPowerComponentCount()).isEqualTo(1);
- assertThat(userBatteryConsumer.getCustomPowerComponentName(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo("FOO");
- } else {
- fail("Unexpected user ID " + userBatteryConsumer.getUserId());
+ if (includesUserBatteryConsumers) {
+ assertThat(userBatteryConsumers).hasSize(1);
+ for (UserBatteryConsumer userBatteryConsumer : userBatteryConsumers) {
+ if (userBatteryConsumer.getUserId() == USER_ID) {
+ assertUserBatteryConsumer(userBatteryConsumer, 42, 10, 20, 30, 40);
+ } else {
+ fail("Unexpected User ID " + userBatteryConsumer.getUserId());
+ }
}
+ } else {
+ assertThat(userBatteryConsumers).isEmpty();
}
+
+ assertAggregateBatteryConsumer(batteryUsageStats,
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS,
+ 10100, 10200, 10300, 10400);
+
+ assertAggregateBatteryConsumer(batteryUsageStats,
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE,
+ 20100, 20200, 20300, 20400);
+ }
+
+ private void assertBatteryUsageStats(BatteryUsageStats batteryUsageStats, int consumedPower,
+ int dischargePercentage, int dischagePowerLower, int dischargePowerUpper,
+ int statsStartTimestamp, int statsEndTimestamp, int statsDuration) {
+ assertThat(batteryUsageStats.getConsumedPower()).isEqualTo(consumedPower);
+ assertThat(batteryUsageStats.getDischargePercentage()).isEqualTo(dischargePercentage);
+ assertThat(batteryUsageStats.getDischargedPowerRange().getLower()).isEqualTo(
+ dischagePowerLower);
+ assertThat(batteryUsageStats.getDischargedPowerRange().getUpper()).isEqualTo(
+ dischargePowerUpper);
+ assertThat(batteryUsageStats.getStatsStartTimestamp()).isEqualTo(statsStartTimestamp);
+ assertThat(batteryUsageStats.getStatsEndTimestamp()).isEqualTo(statsEndTimestamp);
+ assertThat(batteryUsageStats.getStatsDuration()).isEqualTo(statsDuration);
+ }
+
+ private void assertUidBatteryConsumer(UidBatteryConsumer uidBatteryConsumer,
+ int consumedPower, String packageWithHighestDrain, int timeInStateForeground,
+ int timeInStateBackground, int screenPower, int screenPowerModel, int cpuPower,
+ int cpuPowerModel, int customComponentPower, int cpuDuration,
+ int customComponentDuration) {
+ assertThat(uidBatteryConsumer.getConsumedPower()).isEqualTo(consumedPower);
+ assertThat(uidBatteryConsumer.getPackageWithHighestDrain()).isEqualTo(
+ packageWithHighestDrain);
+ assertThat(uidBatteryConsumer.getTimeInStateMs(
+ UidBatteryConsumer.STATE_FOREGROUND)).isEqualTo(timeInStateForeground);
+ assertThat(uidBatteryConsumer.getTimeInStateMs(
+ UidBatteryConsumer.STATE_BACKGROUND)).isEqualTo(timeInStateBackground);
+ assertThat(uidBatteryConsumer.getConsumedPower(
+ BatteryConsumer.POWER_COMPONENT_SCREEN)).isEqualTo(screenPower);
+ assertThat(uidBatteryConsumer.getPowerModel(
+ BatteryConsumer.POWER_COMPONENT_SCREEN)).isEqualTo(screenPowerModel);
+ assertThat(uidBatteryConsumer.getConsumedPower(
+ BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuPower);
+ assertThat(uidBatteryConsumer.getPowerModel(
+ BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuPowerModel);
+ assertThat(uidBatteryConsumer.getConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(customComponentPower);
+ assertThat(uidBatteryConsumer.getUsageDurationMillis(
+ BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuDuration);
+ assertThat(uidBatteryConsumer.getUsageDurationForCustomComponentMillis(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(
+ customComponentDuration);
+ assertThat(uidBatteryConsumer.getCustomPowerComponentCount()).isEqualTo(1);
+ assertThat(uidBatteryConsumer.getCustomPowerComponentName(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo("FOO");
+ }
+
+ private void assertUserBatteryConsumer(UserBatteryConsumer userBatteryConsumer,
+ int userId, int cpuPower, int customComponentPower,
+ int cpuDuration, int customComponentDuration) {
+ assertThat(userBatteryConsumer.getConsumedPower(
+ BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuPower);
+ assertThat(userBatteryConsumer.getConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(customComponentPower);
+ assertThat(userBatteryConsumer.getUsageDurationMillis(
+ BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuDuration);
+ assertThat(userBatteryConsumer.getUsageDurationForCustomComponentMillis(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(
+ customComponentDuration);
+ assertThat(userBatteryConsumer.getCustomPowerComponentCount()).isEqualTo(1);
+ assertThat(userBatteryConsumer.getCustomPowerComponentName(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo("FOO");
+ }
+
+ private void assertAggregateBatteryConsumer(BatteryUsageStats batteryUsageStats,
+ int aggregateBatteryConsumerScopeAllApps, int cpuPower, int customComponentPower,
+ int cpuDuration, int customComponentDuration) {
+ final BatteryConsumer appsBatteryConsumer = batteryUsageStats.getAggregateBatteryConsumer(
+ aggregateBatteryConsumerScopeAllApps);
+ assertThat(appsBatteryConsumer.getConsumedPower(
+ BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuPower);
+ assertThat(appsBatteryConsumer.getConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(customComponentPower);
+ assertThat(appsBatteryConsumer.getUsageDurationMillis(
+ BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuDuration);
+ assertThat(appsBatteryConsumer.getUsageDurationForCustomComponentMillis(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(
+ customComponentDuration);
+ assertThat(appsBatteryConsumer.getCustomPowerComponentCount()).isEqualTo(1);
+ assertThat(appsBatteryConsumer.getCustomPowerComponentName(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo("FOO");
}
}
diff --git a/core/tests/utiltests/src/com/android/internal/util/FileRotatorTest.java b/core/tests/utiltests/src/com/android/internal/util/FileRotatorTest.java
index 3f9e62e..95272132 100644
--- a/core/tests/utiltests/src/com/android/internal/util/FileRotatorTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/FileRotatorTest.java
@@ -29,6 +29,8 @@
import com.android.internal.util.FileRotator.Reader;
import com.android.internal.util.FileRotator.Writer;
+import com.android.internal.util.test.FsUtil;
+
import com.google.android.collect.Lists;
import java.io.DataInputStream;
@@ -38,15 +40,10 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.net.ProtocolException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
-import junit.framework.Assert;
-
-import libcore.io.IoUtils;
-
/**
* Tests for {@link FileRotator}.
*/
@@ -67,7 +64,7 @@
super.setUp();
mBasePath = getContext().getFilesDir();
- IoUtils.deleteContents(mBasePath);
+ FsUtil.deleteContents(mBasePath);
}
public void testEmpty() throws Exception {
diff --git a/data/etc/car/com.android.car.activityresolver.xml b/data/etc/car/com.android.car.activityresolver.xml
index d48bc15..927c738 100644
--- a/data/etc/car/com.android.car.activityresolver.xml
+++ b/data/etc/car/com.android.car.activityresolver.xml
@@ -19,3 +19,4 @@
<permission name="android.permission.MANAGE_USERS"/>
</privapp-permissions>
</permissions>
+
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 04291e3..aa38cc0 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -341,6 +341,8 @@
<!-- Needed for test only -->
<permission name="android.permission.PACKET_KEEPALIVE_OFFLOAD" />
<permission name="android.permission.POWER_SAVER" />
+ <!-- Needed for CTS tests -->
+ <permission name="android.permission.READ_ACTIVE_EMERGENCY_SESSION"/>
<permission name="android.permission.READ_CARRIER_APP_INFO"/>
<permission name="android.permission.READ_FRAME_BUFFER"/>
<permission name="android.permission.READ_LOWPAN_CREDENTIAL"/>
@@ -528,6 +530,11 @@
<permission name="android.permission.CONTROL_VPN"/>
</privapp-permissions>
+ <privapp-permissions package="com.android.wallpaper.livepicker">
+ <permission name="android.permission.SET_WALLPAPER_COMPONENT"/>
+ <permission name="android.permission.BIND_WALLPAPER"/>
+ </privapp-permissions>
+
<privapp-permissions package="com.android.dynsystem">
<permission name="android.permission.REBOOT"/>
<permission name="android.permission.MANAGE_DYNAMIC_SYSTEM"/>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 3c9086d..1803b06 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -103,6 +103,12 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
+ "-2010331310": {
+ "message": "resumeTopActivity: Top activity resumed (dontWaitForPause) %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"-2006946193": {
"message": "setClientVisible: %s clientVisible=%b Callers=%s",
"level": "VERBOSE",
@@ -211,6 +217,12 @@
"group": "WM_DEBUG_WINDOW_ORGANIZER",
"at": "com\/android\/server\/wm\/TaskOrganizerController.java"
},
+ "-1886145147": {
+ "message": "resumeTopActivity: Going to sleep and all paused",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"-1884933373": {
"message": "enableScreenAfterBoot: mDisplayEnabled=%b mForceDisplayEnabled=%b mShowingBootMessages=%b mSystemBooted=%b. %s",
"level": "INFO",
@@ -247,12 +259,6 @@
"group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
"at": "com\/android\/server\/wm\/AppTransition.java"
},
- "-1861864501": {
- "message": "resumeTopActivityLocked: Going to sleep and all paused",
- "level": "DEBUG",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-1844540996": {
"message": " Initial targets: %s",
"level": "VERBOSE",
@@ -325,12 +331,6 @@
"group": "WM_DEBUG_RECENTS_ANIMATIONS",
"at": "com\/android\/server\/wm\/RecentsAnimationController.java"
},
- "-1768090656": {
- "message": "Re-launching after pause: %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-1750384749": {
"message": "Launch on display check: allow launch on public display",
"level": "DEBUG",
@@ -415,12 +415,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "-1655805455": {
- "message": "Enqueue pending stop if needed: %s wasStopping=%b visibleRequested=%b",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-1647332198": {
"message": "remove RecentTask %s when finishing user %d",
"level": "INFO",
@@ -433,6 +427,12 @@
"group": "WM_DEBUG_ADD_REMOVE",
"at": "com\/android\/server\/wm\/ResetTargetTaskHelper.java"
},
+ "-1633115609": {
+ "message": "Key dispatch not paused for screen off",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"-1632122349": {
"message": "Changing surface while display frozen: %s",
"level": "VERBOSE",
@@ -487,6 +487,12 @@
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
+ "-1564228464": {
+ "message": "App died while pausing: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"-1559645910": {
"message": "Looking for task of type=%s, taskAffinity=%s, intent=%s, info=%s, preferredTDA=%s",
"level": "DEBUG",
@@ -565,12 +571,6 @@
"group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/wm\/ActivityStarter.java"
},
- "-1492696222": {
- "message": "App died during pause, not stopping: %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-1480772131": {
"message": "No app or window is requesting an orientation, return %d for display id=%d",
"level": "VERBOSE",
@@ -637,12 +637,24 @@
"group": "WM_DEBUG_ADD_REMOVE",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-1421296808": {
+ "message": "Moving to RESUMED: %s (in existing)",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"-1419762046": {
"message": "moveRootTaskToDisplay: moving taskId=%d to displayId=%d",
"level": "DEBUG",
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
},
+ "-1419461256": {
+ "message": "resumeTopActivity: Resumed %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"-1413901262": {
"message": "startRecentsActivity(): intent=%s",
"level": "DEBUG",
@@ -805,6 +817,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "-1187377055": {
+ "message": "Enqueue pending stop if needed: %s wasStopping=%b visibleRequested=%b",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"-1176488860": {
"message": "SURFACE isSecure=%b: %s",
"level": "INFO",
@@ -919,12 +937,6 @@
"group": "WM_DEBUG_RECENTS_ANIMATIONS",
"at": "com\/android\/server\/wm\/RecentsAnimation.java"
},
- "-1066383762": {
- "message": "Sleep still waiting to pause %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-1060365734": {
"message": "Attempted to add QS dialog window with bad token %s. Aborting.",
"level": "WARN",
@@ -979,6 +991,12 @@
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/WindowState.java"
},
+ "-957060823": {
+ "message": "Moving to PAUSING: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"-951939129": {
"message": "Unregister task organizer=%s uid=%d",
"level": "VERBOSE",
@@ -1231,12 +1249,6 @@
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
- "-672228342": {
- "message": "resumeTopActivityLocked: Top activity resumed %s",
- "level": "DEBUG",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-668956537": {
"message": " THUMBNAIL %s: CREATE",
"level": "INFO",
@@ -1261,11 +1273,11 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
- "-650261962": {
- "message": "Sleep needs to pause %s",
+ "-648891906": {
+ "message": "Activity not running or entered PiP, resuming next.",
"level": "VERBOSE",
"group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
},
"-641258376": {
"message": "realStartActivityLocked: Skipping start of r=%s some activities pausing...",
@@ -1303,12 +1315,6 @@
"group": "WM_DEBUG_BOOT",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "-606328116": {
- "message": "resumeTopActivityLocked: Top activity resumed (dontWaitForPause) %s",
- "level": "DEBUG",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-597091183": {
"message": "Delete TaskDisplayArea uid=%d",
"level": "VERBOSE",
@@ -1369,23 +1375,17 @@
"group": "WM_SHOW_TRANSACTIONS",
"at": "com\/android\/server\/wm\/WindowAnimator.java"
},
- "-533690126": {
- "message": "resumeTopActivityLocked: Resumed %s",
- "level": "DEBUG",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-532081937": {
"message": " Commit activity becoming invisible: %s",
"level": "VERBOSE",
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
- "-527683022": {
- "message": "resumeTopActivityLocked: Skip resume: some activity pausing.",
+ "-521613870": {
+ "message": "App died during pause, not stopping: %s",
"level": "VERBOSE",
"group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
},
"-519504830": {
"message": "applyAnimation: anim=%s nextAppTransition=ANIM_CUSTOM transit=%s isEntrance=%b Callers=%s",
@@ -1471,18 +1471,6 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/DisplayRotation.java"
},
- "-427457280": {
- "message": "App died while pausing: %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
- "-417514857": {
- "message": "Key dispatch not paused for screen off",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-415865166": {
"message": "findFocusedWindow: Found new focus @ %s",
"level": "VERBOSE",
@@ -1591,11 +1579,17 @@
"group": "WM_DEBUG_LOCKTASK",
"at": "com\/android\/server\/wm\/LockTaskController.java"
},
- "-303497363": {
- "message": "reparent: moving activity=%s to task=%d at %d",
+ "-312353598": {
+ "message": "Executing finish of activity: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
+ "-310337305": {
+ "message": "Activity config changed during resume: %s, new next: %s",
"level": "INFO",
- "group": "WM_DEBUG_ADD_REMOVE",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
},
"-302468788": {
"message": "Expected target rootTask=%s to be top most but found rootTask=%s",
@@ -1615,12 +1609,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "-279436615": {
- "message": "Moving to PAUSING: %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-262984451": {
"message": "Relaunch failed %s",
"level": "INFO",
@@ -1645,12 +1633,6 @@
"group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
- "-234244777": {
- "message": "Activity config changed during resume: %s, new next: %s",
- "level": "INFO",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-230587670": {
"message": "SyncGroup %d: Unfinished container: %s",
"level": "VERBOSE",
@@ -1717,12 +1699,6 @@
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
- "-118786523": {
- "message": "Resume failed; resetting state to %s: %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-116086365": {
"message": "******************** ENABLING SCREEN!",
"level": "INFO",
@@ -1771,6 +1747,12 @@
"group": "WM_SHOW_TRANSACTIONS",
"at": "com\/android\/server\/wm\/Session.java"
},
+ "-80004683": {
+ "message": "Resume failed; resetting state to %s: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"-50336993": {
"message": "moveFocusableActivityToTop: activity=%s",
"level": "DEBUG",
@@ -1903,12 +1885,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowContextListenerController.java"
},
- "94402792": {
- "message": "Moving to RESUMED: %s (in existing)",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"95216706": {
"message": "hideIme target: %s ",
"level": "DEBUG",
@@ -1927,6 +1903,12 @@
"group": "WM_DEBUG_APP_TRANSITIONS",
"at": "com\/android\/server\/wm\/AppTransitionController.java"
},
+ "102618780": {
+ "message": "resumeTopActivity: Pausing %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"108170907": {
"message": "Add starting %s: startingData=%s",
"level": "VERBOSE",
@@ -2167,6 +2149,18 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "327461496": {
+ "message": "Complete pause: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
+ "341055768": {
+ "message": "resumeTopActivity: Skip resume: need to start pausing",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"342460966": {
"message": "DRAG %s: pos=(%d,%d)",
"level": "INFO",
@@ -2221,11 +2215,11 @@
"group": "WM_DEBUG_BOOT",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "391189028": {
- "message": "pauseBackTasks: task=%s mResumedActivity=%s",
- "level": "DEBUG",
+ "378825104": {
+ "message": "Enqueueing pending pause: %s",
+ "level": "VERBOSE",
"group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/TaskDisplayArea.java"
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
},
"397105698": {
"message": "grantEmbeddedWindowFocus remove request for win=%s dropped since no candidate was found",
@@ -2359,6 +2353,12 @@
"group": "WM_SHOW_TRANSACTIONS",
"at": "com\/android\/server\/wm\/WindowSurfaceController.java"
},
+ "573582981": {
+ "message": "reparent: moving activity=%s to new task fragment in task=%d at %d",
+ "level": "INFO",
+ "group": "WM_DEBUG_ADD_REMOVE",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
"579298675": {
"message": "Moving to DESTROYED: %s (removed from history)",
"level": "VERBOSE",
@@ -2461,6 +2461,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "660908897": {
+ "message": "Auto-PIP allowed, entering PIP mode directly: %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"662572728": {
"message": "Attempted to add a toast window with bad token %s. Aborting.",
"level": "WARN",
@@ -2479,12 +2485,24 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "669361121": {
+ "message": "Sleep still need to stop %d activities",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"674932310": {
"message": "Setting Intent of %s to target %s",
"level": "VERBOSE",
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/Task.java"
},
+ "675705156": {
+ "message": "resumeTopActivity: Top activity resumed %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"685047360": {
"message": "Resizing window %s",
"level": "VERBOSE",
@@ -2515,12 +2533,6 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java"
},
- "709500946": {
- "message": "resumeTopActivityLocked: Skip resume: need to start pausing",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"715749922": {
"message": "Allowlisting %d:%s",
"level": "WARN",
@@ -2623,12 +2635,6 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
},
- "897964776": {
- "message": "Complete pause: %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"898863925": {
"message": "Attempted to add QS dialog window with unknown token %s. Aborting.",
"level": "WARN",
@@ -2653,6 +2659,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/WindowStateAnimator.java"
},
+ "935418348": {
+ "message": "resumeTopActivity: Skip resume: some activity pausing.",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"950074526": {
"message": "setLockTaskMode: Can't lock due to auth",
"level": "WARN",
@@ -2701,11 +2713,11 @@
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java"
},
- "988389910": {
- "message": "resumeTopActivityLocked: Pausing %s",
- "level": "DEBUG",
+ "987903142": {
+ "message": "Sleep needs to pause %s",
+ "level": "VERBOSE",
"group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
},
"996960396": {
"message": "Starting Transition %d",
@@ -2713,18 +2725,18 @@
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
- "1001509841": {
- "message": "Auto-PIP allowed, entering PIP mode directly: %s",
- "level": "DEBUG",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"1001904964": {
"message": "***** BOOT TIMEOUT: forcing display enabled",
"level": "WARN",
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "1011462000": {
+ "message": "Re-launching after pause: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"1023413388": {
"message": "Finish waiting for pause of: %s",
"level": "VERBOSE",
@@ -3157,12 +3169,6 @@
"group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
"at": "com\/android\/server\/wm\/WindowContainer.java"
},
- "1585450696": {
- "message": "resumeTopActivityLocked: Restarting %s",
- "level": "DEBUG",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"1589610525": {
"message": "applyAnimation NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS: anim=%s transit=%s isEntrance=true Callers=%s",
"level": "VERBOSE",
@@ -3337,6 +3343,12 @@
"group": "WM_DEBUG_APP_TRANSITIONS",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "1814259538": {
+ "message": "pauseBackTasks: taskFrag=%s mResumedActivity=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskDisplayArea.java"
+ },
"1822314934": {
"message": "Expected target rootTask=%s to restored behind rootTask=%s but it is behind rootTask=%s",
"level": "WARN",
@@ -3379,18 +3391,6 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
- "1837992242": {
- "message": "Executing finish of activity: %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
- "1847414670": {
- "message": "Activity not running or entered PiP, resuming next.",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"1853793312": {
"message": "Notify removed startingWindow %s",
"level": "VERBOSE",
@@ -3403,6 +3403,12 @@
"group": "WM_DEBUG_FOCUS",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "1856783490": {
+ "message": "resumeTopActivity: Restarting %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"1865125884": {
"message": "finishScreenTurningOn: mAwake=%b, mScreenOnEarly=%b, mScreenOnFully=%b, mKeyguardDrawComplete=%b, mWindowManagerDrawComplete=%b",
"level": "DEBUG",
@@ -3415,30 +3421,24 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "1884961873": {
- "message": "Sleep still need to stop %d activities",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"1891501279": {
"message": "cancelAnimation(): reason=%s",
"level": "DEBUG",
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
- "1894239744": {
- "message": "Enqueueing pending pause: %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"1903353011": {
"message": "notifyAppStopped: %s",
"level": "VERBOSE",
"group": "WM_DEBUG_ADD_REMOVE",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "1912291550": {
+ "message": "Sleep still waiting to pause %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"1918448345": {
"message": "Task appeared taskId=%d",
"level": "VERBOSE",
diff --git a/data/keyboards/Vendor_0171_Product_0419.kl b/data/keyboards/Vendor_0171_Product_0419.kl
new file mode 100644
index 0000000..05a25f0
--- /dev/null
+++ b/data/keyboards/Vendor_0171_Product_0419.kl
@@ -0,0 +1,55 @@
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Amazon Luna Controller
+#
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Left and right stick.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x02 Z flat 4096
+axis 0x05 RZ flat 4096
+
+# Triggers.
+axis 0x0a LTRIGGER
+axis 0x09 RTRIGGER
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+
+# Action button (circle icon, left of the Home button)
+key 158 BUTTON_SELECT
+
+# Home button (branded button in the center of the controller)
+key 172 BUTTON_MODE
+
+# Menu button (hamburger icon, right of the Home button)
+key 315 BUTTON_START
+
+# Alexa Push-To-Talk button (microphone icon, below the Home button)
+key 217 MEDIA_RECORD
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/UnattributedNoteOpCallChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/UnattributedNoteOpCallChecker.java
new file mode 100644
index 0000000..3d7b94f
--- /dev/null
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/UnattributedNoteOpCallChecker.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.errorprone.bugpatterns.android;
+
+import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
+import static com.google.errorprone.matchers.Matchers.instanceMethod;
+import static com.google.errorprone.matchers.Matchers.methodInvocation;
+
+import com.google.auto.service.AutoService;
+import com.google.errorprone.BugPattern;
+import com.google.errorprone.VisitorState;
+import com.google.errorprone.bugpatterns.BugChecker;
+import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
+import com.google.errorprone.matchers.Description;
+import com.google.errorprone.matchers.Matcher;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.MethodInvocationTree;
+
+@AutoService(BugChecker.class)
+@BugPattern(
+ name = "AndroidFrameworkUnattributedNoteOpCall",
+ summary = "Verifies that a noteOp() call is attributed",
+ severity = WARNING)
+public final class UnattributedNoteOpCallChecker extends BugChecker
+ implements MethodInvocationTreeMatcher {
+
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEOP_CALL_1 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("noteOp(int,int,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEOP_CALL_2 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("noteOp(java.lang.String,int,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEOP_CALL_3 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("noteOp(int)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEOPNOTHROW_CALL_1 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("noteOpNoThrow(int,int,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEOPNOTHROW_CALL_2 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("noteOpNoThrow(java.lang.String,int,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_STARTOP_CALL_1 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("startOp(java.lang.String,int,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_STARTOP_CALL_2 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("startOp(int,int,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_STARTOP_CALL_3 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("startOp(int)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_STARTOP_CALL_4 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("startOp(int,int,java.lang.String,boolean)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_STARTOPNOTHROW_CALL_1 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("startOpNoThrow(java.lang.String,int,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_STARTOPNOTHROW_CALL_2 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("startOpNoThrow(int,int,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_STARTOPNOTHROW_CALL_3 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("startOpNoThrow(int,int,java.lang.String,boolean)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEPROXYOP_CALL_1 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("noteProxyOp(java.lang.String,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEPROXYOP_CALL_2 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("noteProxyOp(int,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEPROXYOPNOTHROW_CALL_1 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("noteProxyOpNoThrow(java.lang.String,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEPROXYOPNOTHROW_CALL_2 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("noteProxyOpNoThrow(java.lang.String,java.lang.String,int)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_FINISHOP_CALL_1 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("finishOp(int)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_FINISHOP_CALL_2 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("finishOp(java.lang.String,int,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_FINISHOP_CALL_3 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("finishOp(int,int,java.lang.String)"));
+
+ @Override
+ public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
+ if (UNATTRIBUTED_NOTEOP_CALL_1.matches(tree, state)
+ || UNATTRIBUTED_NOTEOP_CALL_2.matches(tree, state)
+ || UNATTRIBUTED_NOTEOP_CALL_3.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Unattributed noteOp call! Please use noteOp(int, String, String, String) or noteOp(int, CallerIdentity)")
+ .build();
+ }
+ if (UNATTRIBUTED_NOTEOPNOTHROW_CALL_1.matches(tree, state)
+ || UNATTRIBUTED_NOTEOPNOTHROW_CALL_2.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Unattributed noteOpNoThrow call! Please use noteOpNoThrow(String, int, String, String, String)")
+ .build();
+ }
+ if (UNATTRIBUTED_STARTOP_CALL_1.matches(tree, state)
+ || UNATTRIBUTED_STARTOP_CALL_2.matches(tree, state)
+ || UNATTRIBUTED_STARTOP_CALL_3.matches(tree, state)
+ || UNATTRIBUTED_STARTOP_CALL_4.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Unattributed startOp call! Please use startOp(int, int, String, boolean, String, String)")
+ .build();
+ }
+ if (UNATTRIBUTED_STARTOPNOTHROW_CALL_1.matches(tree, state)
+ || UNATTRIBUTED_STARTOPNOTHROW_CALL_2.matches(tree, state)
+ || UNATTRIBUTED_STARTOPNOTHROW_CALL_3.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Unattributed startOpNoThrow call!")
+ .build();
+ }
+ if (UNATTRIBUTED_NOTEPROXYOP_CALL_1.matches(tree, state)
+ || UNATTRIBUTED_NOTEPROXYOP_CALL_2.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Unattributed noteProxyOp call!")
+ .build();
+ }
+ if (UNATTRIBUTED_NOTEPROXYOPNOTHROW_CALL_1.matches(tree, state)
+ || UNATTRIBUTED_NOTEPROXYOPNOTHROW_CALL_2.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Unattributed noteProxyOpNoThrow call!")
+ .build();
+ }
+ if (UNATTRIBUTED_FINISHOP_CALL_1.matches(tree, state)
+ || UNATTRIBUTED_FINISHOP_CALL_2.matches(tree, state)
+ || UNATTRIBUTED_FINISHOP_CALL_3.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Unattributed finishOp call!")
+ .build();
+ }
+
+
+
+ return Description.NO_MATCH;
+ }
+}
diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UnattributedNoteOpCallCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UnattributedNoteOpCallCheckerTest.java
new file mode 100644
index 0000000..9a98c7c
--- /dev/null
+++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UnattributedNoteOpCallCheckerTest.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.errorprone.bugpatterns.android;
+
+import com.google.errorprone.CompilationTestHelper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class UnattributedNoteOpCallCheckerTest {
+ private CompilationTestHelper mCompilationHelper;
+
+ @Before
+ public void setUp() {
+ mCompilationHelper = CompilationTestHelper.newInstance(
+ UnattributedNoteOpCallChecker.class, getClass());
+ }
+
+ @Test
+ public void testNoteOp() {
+ mCompilationHelper
+ .addSourceFile("/android/app/AppOpsManager.java")
+ .addSourceLines("Example.java",
+ "import android.app.AppOpsManager;",
+ "public class Example {",
+ " void example() {",
+ " AppOpsManager mAppOps = new AppOpsManager();",
+ " mAppOps.noteOp(\"foo\", 0, \"bar\", \"baz\", \"qux\");",
+ " mAppOps.noteOp(0, 0, \"bar\", \"baz\", \"qux\");",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.noteOp(1, 2, \"foo\");",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.noteOp(\"foo\", 1, \"bar\");",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.noteOp(1);",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testNoteOpNoThrow() {
+ mCompilationHelper
+ .addSourceFile("/android/app/AppOpsManager.java")
+ .addSourceLines("Example.java",
+ "import android.app.AppOpsManager;",
+ "public class Example {",
+ " void example() {",
+ " AppOpsManager mAppOps = new AppOpsManager();",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.noteOpNoThrow(0, 1, \"foo\");",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.noteOpNoThrow(\"foo\", 1, \"bar\");",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testStartOp() {
+ mCompilationHelper
+ .addSourceFile("/android/app/AppOpsManager.java")
+ .addSourceLines("Example.java",
+ "import android.app.AppOpsManager;",
+ "public class Example {",
+ " void example() {",
+ " AppOpsManager mAppOps = new AppOpsManager();",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.startOp(0, 0, \"bar\", true);",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.startOp(1, 2, \"foo\");",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.startOp(\"foo\", 1, \"bar\");",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.startOp(1);",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testStartOpNoThrow() {
+ mCompilationHelper
+ .addSourceFile("/android/app/AppOpsManager.java")
+ .addSourceLines("Example.java",
+ "import android.app.AppOpsManager;",
+ "public class Example {",
+ " void example() {",
+ " AppOpsManager mAppOps = new AppOpsManager();",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.startOpNoThrow(0, 0, \"bar\", true);",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.startOpNoThrow(1, 2, \"foo\");",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.startOpNoThrow(\"foo\", 1, \"bar\");",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testNoteProxyOp() {
+ mCompilationHelper
+ .addSourceFile("/android/app/AppOpsManager.java")
+ .addSourceLines("Example.java",
+ "import android.app.AppOpsManager;",
+ "public class Example {",
+ " void example() {",
+ " AppOpsManager mAppOps = new AppOpsManager();",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.noteProxyOp(1, \"foo\");",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.noteProxyOp(\"foo\", \"bar\");",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testNoteProxyOpNoThrow() {
+ mCompilationHelper
+ .addSourceFile("/android/app/AppOpsManager.java")
+ .addSourceLines("Example.java",
+ "import android.app.AppOpsManager;",
+ "public class Example {",
+ " void example() {",
+ " AppOpsManager mAppOps = new AppOpsManager();",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.noteProxyOpNoThrow(\"foo\", \"bar\");",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.noteProxyOpNoThrow(\"foo\", \"bar\", 1);",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testFinishOp() {
+ mCompilationHelper
+ .addSourceFile("/android/app/AppOpsManager.java")
+ .addSourceLines("Example.java",
+ "import android.app.AppOpsManager;",
+ "public class Example {",
+ " void example() {",
+ " AppOpsManager mAppOps = new AppOpsManager();",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.finishOp(1, 2, \"foo\");",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.finishOp(\"foo\", 1, \"bar\");",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.finishOp(1);",
+ " }",
+ "}")
+ .doTest();
+ }
+
+
+
+}
diff --git a/errorprone/tests/res/android/app/AppOpsManager.java b/errorprone/tests/res/android/app/AppOpsManager.java
new file mode 100644
index 0000000..216270c
--- /dev/null
+++ b/errorprone/tests/res/android/app/AppOpsManager.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+public class AppOpsManager {
+
+ public int noteOp(String op, int uid, String packageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int noteOp(int op) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int noteOp(int op, int uid, String packageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int noteOp(String op, int uid, String packageName,
+ String attributionTag, String message) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int noteOp(int op, int uid, String packageName,
+ String attributionTag, String message) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int noteOpNoThrow(String op, int uid, String packageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int noteOpNoThrow(int op, int uid, String packageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int startOp(int op) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int startOp(int op, int uid, String packageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int startOp(int op, int uid, String packageName, boolean startIfModeDefault) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int startOp(String op, int uid, String packageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int startOpNoThrow(String op, int uid, String packageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int startOpNoThrow(int op, int uid, String packageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int startOpNoThrow(int op, int uid, String packageName, boolean startIfModeDefault) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int noteProxyOp(String op, String proxiedPackageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int noteProxyOp(int op, String proxiedPackageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int noteProxyOpNoThrow(String op, String proxiedPackageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int noteProxyOpNoThrow(String op, String proxiedPackageName,
+ int proxiedUid) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void finishOp(int op) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void finishOp(String op, int uid, String packageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void finishOp(int op, int uid, String packageName) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml
index ea634cf..69aa31e 100644
--- a/libs/WindowManager/Shell/res/values-af/strings.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Slaan oor na volgende"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Slaan oor na vorige"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Verander grootte"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Hou vas"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Laat los"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Program sal dalk nie met verdeelde skerm werk nie."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Program steun nie verdeelde skerm nie."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Program sal dalk nie op \'n sekondêre skerm werk nie."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Borrel"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Bestuur"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Borrel is toegemaak."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Tik om hierdie program te herbegin en maak volskerm oop."</string>
+ <string name="got_it" msgid="4428750913636945527">"Het dit"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml
index e4628d7..c754e3ca 100644
--- a/libs/WindowManager/Shell/res/values-am/strings.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"ወደ ቀጣይ ዝለል"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"ወደ ቀዳሚ ዝለል"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"መጠን ይቀይሩ"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"መተግበሪያ ከተከፈለ ማያ ገጽ ጋር ላይሠራ ይችላል"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"መተግበሪያው የተከፈለ ማያ ገጽን አይደግፍም።"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"መተግበሪያ በሁለተኛ ማሳያ ላይ ላይሠራ ይችላል።"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"አረፋ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ያቀናብሩ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"አረፋ ተሰናብቷል።"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"ይህን መተግበሪያ ዳግም ለማስነሳት መታ ያድርጉ እና ወደ ሙሉ ማያ ገጽ ይሂዱ።"</string>
+ <string name="got_it" msgid="4428750913636945527">"ገባኝ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
index 7b5bda7..ac72a3d 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"التخطي إلى التالي"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"التخطي إلى السابق"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"تغيير الحجم"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"إخفاء"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"إظهار"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"قد لا يعمل التطبيق بشكل سليم في وضع \"تقسيم الشاشة\"."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"التطبيق لا يتيح تقسيم الشاشة."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"قد لا يعمل التطبيق على شاشة عرض ثانوية."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"فقاعة"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"إدارة"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"تم إغلاق الفقاعة."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"انقر لإعادة تشغيل هذا التطبيق والانتقال إلى وضع ملء الشاشة."</string>
+ <string name="got_it" msgid="4428750913636945527">"حسنًا"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index 47294c4..1ace3cd 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"পৰৱৰ্তী মিডিয়ালৈ যাওক"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"আগৰটো মিডিয়ালৈ যাওক"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"আকাৰ সলনি কৰক"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"লুকুৱাওক"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"দেখুৱাওক"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"এপ্টোৱে বিভাজিত স্ক্ৰীনৰ সৈতে কাম নকৰিব পাৰে।"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"এপটোৱে বিভাজিত স্ক্ৰীণ সমৰ্থন নকৰে।"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"গৌণ ডিছপ্লেত এপে সঠিকভাৱে কাম নকৰিব পাৰে।"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"বাবল"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"পৰিচালনা কৰক"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"বাবল অগ্ৰাহ্য কৰা হৈছে"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"এপ্টো ৰিষ্টাৰ্ট কৰিবলৈ আৰু পূৰ্ণ স্ক্ৰীন ব্যৱহাৰ কৰিবলৈ টিপক।"</string>
+ <string name="got_it" msgid="4428750913636945527">"বুজি পালোঁ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
index 923ff79..6d3e0a9 100644
--- a/libs/WindowManager/Shell/res/values-az/strings.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Növbətiyə keçin"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Əvvəlkinə keçin"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ölçüsünü dəyişin"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Güvənli məkanda saxlayın"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Güvənli məkandan çıxarın"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Tətbiq bölünmüş ekran ilə işləməyə bilər."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Tətbiq ekran bölünməsini dəstəkləmir."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Tətbiq ikinci ekranda işləməyə bilər."</string>
@@ -43,10 +45,10 @@
<string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Yuxarı 50%"</string>
<string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Yuxarı 30%"</string>
<string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Aşağı tam ekran"</string>
- <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Bir əlli rejimdən istifadə edilir"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Birəlli rejim istifadəsi"</string>
<string name="one_handed_tutorial_description" msgid="3486582858591353067">"Çıxmaq üçün ekranın aşağısından yuxarıya doğru sürüşdürün və ya tətbiqin yuxarısında istənilən yerə toxunun"</string>
- <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Bir əlli rejimi başladın"</string>
- <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Bir əlli rejimdən çıxın"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Birəlli rejim başlasın"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Birəlli rejimdən çıxın"</string>
<string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> yumrucuqları üçün ayarlar"</string>
<string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Kənara çıxma"</string>
<string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Yenidən dəstəyə əlavə edin"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Qabarcıq"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"İdarə edin"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Qabarcıqdan imtina edilib."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Bu tətbiqi sıfırlayaraq tam ekrana keçmək üçün toxunun."</string>
+ <string name="got_it" msgid="4428750913636945527">"Anladım"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
index 02e609cd..358da25 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Pređi na sledeće"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Pređi na prethodno"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Promenite veličinu"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stavite u tajnu memoriju"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Uklonite iz tajne memorije"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacija možda neće raditi sa podeljenim ekranom."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacija ne podržava podeljeni ekran."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija možda neće funkcionisati na sekundarnom ekranu."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljajte"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić je odbačen."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Dodirnite da biste restartovali aplikaciju i prešli u režim celog ekrana."</string>
+ <string name="got_it" msgid="4428750913636945527">"Važi"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
index ccea318..7a934cc 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Перайсці да наступнага"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Перайсці да папярэдняга"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Змяніць памер"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Схаваць"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Паказаць"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Праграма можа не працаваць у рэжыме падзеленага экрана."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Праграма не падтрымлівае функцыю дзялення экрана."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Праграма можа не працаваць на дадатковых экранах."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Усплывальнае апавяшчэнне"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Кіраваць"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Усплывальнае апавяшчэнне адхілена."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Націсніце, каб перазапусціць гэту праграму і перайсці ў поўнаэкранны рэжым."</string>
+ <string name="got_it" msgid="4428750913636945527">"Зразумела"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
index d29660b..02930b1 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Към следващия елемент"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Към предишния елемент"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Преоразмеряване"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Съхраняване"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Отмяна на съхраняването"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Приложението може да не работи в режим на разделен екран."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Приложението не поддържа разделен екран."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Възможно е приложението да не работи на алтернативни дисплеи."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Балонче"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Управление"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Балончето е отхвърлено."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Докоснете, за да рестартирате това приложение в режим на цял екран."</string>
+ <string name="got_it" msgid="4428750913636945527">"Разбрах"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index 84bcaf9..b35e179 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"এগিয়ে যাওয়ার জন্য এড়িয়ে যান"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"পিছনে যাওয়ার জন্য এড়িয়ে যান"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"রিসাইজ করুন"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"স্ট্যাস করুন"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"আনস্ট্যাস করুন"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"অ্যাপটি স্প্লিট স্ক্রিনে কাজ নাও করতে পারে।"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"অ্যাপ্লিকেশান বিভক্ত-স্ক্রিন সমর্থন করে না৷"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"সেকেন্ডারি ডিসপ্লেতে অ্যাপটি কাজ নাও করতে পারে।"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"বাবল"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ম্যানেজ করুন"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"বাবল বাতিল করা হয়েছে।"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"এই অ্যাপ রিস্টার্ট করতে ট্যাপ করুন ও \'ফুল-স্ক্রিন\' মোড ব্যবহার করুন।"</string>
+ <string name="got_it" msgid="4428750913636945527">"বুঝেছি"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index 85e08d7..14d90a4 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Preskoči na sljedeći"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Preskoči na prethodni"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Promjena veličine"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stavljanje u stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Vađenje iz stasha"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacija možda neće raditi na podijeljenom ekranu."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacija ne podržava dijeljenje ekrana."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija možda neće raditi na sekundarnom ekranu."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljaj"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić je odbačen."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Dodirnite da ponovo pokrenete ovu aplikaciju i aktivirate prikaz preko cijelog ekrana."</string>
+ <string name="got_it" msgid="4428750913636945527">"Razumijem"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
index a80b7fb..9cffbdd 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Ves al següent"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Torna a l\'anterior"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Canvia la mida"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Amaga"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Deixa d\'amagar"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"És possible que l\'aplicació no funcioni amb la pantalla dividida."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"L\'aplicació no admet la pantalla dividida."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"És possible que l\'aplicació no funcioni en una pantalla secundària."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bombolla"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gestiona"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"La bombolla s\'ha ignorat."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Toca per reiniciar aquesta aplicació i passar a pantalla completa."</string>
+ <string name="got_it" msgid="4428750913636945527">"Entesos"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index e8257bc..9b5206a 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Přeskočit na další"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Přeskočit na předchozí"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Změnit velikost"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Uložit"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Zrušit uložení"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikace v režimu rozdělené obrazovky nemusí fungovat."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikace nepodporuje režim rozdělené obrazovky."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikace na sekundárním displeji nemusí fungovat."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bublina"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Spravovat"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bublina byla zavřena."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Klepnutím aplikaci restartujete a přejdete na režim celé obrazovky"</string>
+ <string name="got_it" msgid="4428750913636945527">"Rozumím"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
index 17f8286..a06abf1 100644
--- a/libs/WindowManager/Shell/res/values-da/strings.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Gå videre til næste"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Gå til forrige"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Rediger størrelse"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Skjul"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Vis"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Appen fungerer muligvis ikke i opdelt skærm."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Appen understøtter ikke opdelt skærm."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Appen fungerer muligvis ikke på sekundære skærme."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Boble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Administrer"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Boblen blev lukket."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Tryk for at genstarte denne app, og gå til fuld skærm."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index f04796a..c5e79f8 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Vorwärts springen"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Rückwärts springen"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Größe anpassen"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"In Stash legen"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Aus Stash entfernen"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Die App funktioniert unter Umständen bei geteiltem Bildschirmmodus nicht."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Das Teilen des Bildschirms wird in dieser App nicht unterstützt."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Die App funktioniert auf einem sekundären Display möglicherweise nicht."</string>
@@ -60,13 +62,15 @@
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Bubble schließen"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Unterhaltung nicht als Bubble anzeigen"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Bubbles zum Chatten verwenden"</string>
- <string name="bubbles_user_education_description" msgid="4215862563054175407">"Neue Unterhaltungen erscheinen als unverankerte Symbole, \"Bubbles\" genannt. Wenn du die Bubble öffnen möchtest, tippe sie an. Wenn du sie verschieben möchtest, zieh an ihr."</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Neue Unterhaltungen erscheinen als unverankerte Symbole, „Bubbles“ genannt. Wenn du eine Bubble öffnen möchtest, tippe sie an. Wenn du sie verschieben möchtest, zieh an ihr."</string>
<string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Bubble-Einstellungen festlegen"</string>
- <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tippe auf \"Verwalten\", um Bubbles für diese App zu deaktivieren"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tippe auf „Verwalten“, um Bubbles für diese App zu deaktivieren"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Keine kürzlich geschlossenen Bubbles"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Hier werden aktuelle und geschlossene Bubbles angezeigt"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Verwalten"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble verworfen."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Tippe, um die App im Vollbildmodus neu zu starten."</string>
+ <string name="got_it" msgid="4428750913636945527">"Ok"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
index cc329e8..fc397c5 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Μετάβαση στο επόμενο"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Μετάβαση στο προηγούμενο"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Αλλαγή μεγέθους"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Απόκρυψη"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Κατάργηση απόκρυψης"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Η εφαρμογή ενδέχεται να μην λειτουργεί με διαχωρισμό οθόνης."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Η εφαρμογή δεν υποστηρίζει διαχωρισμό οθόνης."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Η εφαρμογή ίσως να μην λειτουργήσει σε δευτερεύουσα οθόνη."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Συννεφάκι"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Διαχείριση"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Το συννεφάκι παραβλέφθηκε."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Πατήστε για επανεκκίνηση αυτής της εφαρμογής και ενεργοποίηση πλήρους οθόνης."</string>
+ <string name="got_it" msgid="4428750913636945527">"Το κατάλαβα"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
index 90c71c0..a4f287f 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Skip to next"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Skip to previous"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Resize"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"App may not work with split-screen."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App does not support split-screen."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Tap to restart this app and go full screen."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
index 90c71c0..a4f287f 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Skip to next"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Skip to previous"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Resize"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"App may not work with split-screen."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App does not support split-screen."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Tap to restart this app and go full screen."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
index 90c71c0..a4f287f 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Skip to next"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Skip to previous"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Resize"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"App may not work with split-screen."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App does not support split-screen."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Tap to restart this app and go full screen."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
index 90c71c0..a4f287f 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Skip to next"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Skip to previous"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Resize"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"App may not work with split-screen."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App does not support split-screen."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Tap to restart this app and go full screen."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
index d8b5b400..87210d5 100644
--- a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Skip to next"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Skip to previous"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Resize"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"App may not work with split-screen."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App does not support split-screen."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Tap to restart this app and go full screen."</string>
+ <string name="got_it" msgid="4428750913636945527">"Got it"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
index 7244b1a..ebe41e8 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Siguiente"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Anterior"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Cambiar el tamaño"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Almacenar de manera segura"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Dejar de almacenar de manera segura"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Es posible que la app no funcione en el modo de pantalla dividida."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"La app no es compatible con la función de pantalla dividida."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Es posible que la app no funcione en una pantalla secundaria."</string>
@@ -58,7 +60,7 @@
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Ubicar abajo a la derecha"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Configuración de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Descartar burbuja"</string>
- <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"No mostrar la conversación en burbujas"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"No mostrar la conversación en burbuja"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat con burbujas"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Las conversaciones nuevas aparecen como elementos flotantes o burbujas. Presiona para abrir la burbuja. Arrástrala para moverla."</string>
<string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Controla las burbujas"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Cuadro"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Administrar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Se descartó el cuadro."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Presiona para reiniciar esta app y acceder al modo de pantalla completa."</string>
+ <string name="got_it" msgid="4428750913636945527">"Entendido"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index 65e75bd..5949099 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Saltar al siguiente"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Volver al anterior"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Cambiar tamaño"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Esconder"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"No esconder"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Es posible que la aplicación no funcione con la pantalla dividida."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"La aplicación no admite la pantalla dividida."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Es posible que la aplicación no funcione en una pantalla secundaria."</string>
@@ -43,10 +45,10 @@
<string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Superior 50%"</string>
<string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Superior 30%"</string>
<string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Pantalla inferior completa"</string>
- <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Utilizar el modo una mano"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Usar Modo una mano"</string>
<string name="one_handed_tutorial_description" msgid="3486582858591353067">"Para salir, desliza el dedo hacia arriba desde la parte inferior de la pantalla o toca cualquier zona que haya encima de la aplicación"</string>
- <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Iniciar modo una mano"</string>
- <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Salir del modo una mano"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Iniciar Modo una mano"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Salir del Modo una mano"</string>
<string name="bubbles_settings_button_description" msgid="1301286017420516912">"Ajustes de las burbujas de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Menú adicional"</string>
<string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Volver a añadir a la pila"</string>
@@ -60,7 +62,7 @@
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Cerrar burbuja"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"No mostrar conversación en burbuja"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatea con burbujas"</string>
- <string name="bubbles_user_education_description" msgid="4215862563054175407">"Las conversaciones nuevas aparecen como iconos flotantes llamadas \"burbujas\". Toca para abrir la burbuja. Arrastra para moverla."</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Las conversaciones nuevas aparecen como iconos flotantes llamadas \"burbujas\". Toca una burbuja para abrirla. Arrástrala para moverla."</string>
<string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Controla las burbujas"</string>
<string name="bubbles_user_education_manage" msgid="3460756219946517198">"Toca Gestionar para desactivar las burbujas de esta aplicación"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Entendido"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Burbuja"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Burbuja cerrada."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Toca para reiniciar esta aplicación e ir a la pantalla completa."</string>
+ <string name="got_it" msgid="4428750913636945527">"Entendido"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
index 0ccfcfe..8330981 100644
--- a/libs/WindowManager/Shell/res/values-et/strings.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Järgmise juurde"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Eelmise juurde"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Suuruse muutmine"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Pane hoidlasse"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Eemalda hoidlast"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Rakendus ei pruugi poolitatud ekraaniga töötada."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Rakendus ei toeta jagatud ekraani."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Rakendus ei pruugi teisesel ekraanil töötada."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Mull"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Halda"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Mullist loobuti."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Puudutage rakenduse taaskäivitamiseks ja täisekraanrežiimi aktiveerimiseks."</string>
+ <string name="got_it" msgid="4428750913636945527">"Selge"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml
index 6682ea8..6649769 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -25,9 +25,11 @@
<string name="pip_notification_message" msgid="8854051911700302620">"Ez baduzu nahi <xliff:g id="NAME">%s</xliff:g> zerbitzuak eginbide hori erabiltzea, sakatu hau ezarpenak ireki eta aukera desaktibatzeko."</string>
<string name="pip_play" msgid="3496151081459417097">"Erreproduzitu"</string>
<string name="pip_pause" msgid="690688849510295232">"Pausatu"</string>
- <string name="pip_skip_to_next" msgid="8403429188794867653">"Saltatu hurrengora"</string>
- <string name="pip_skip_to_prev" msgid="7172158111196394092">"Saltatu aurrekora"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Joan hurrengora"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Joan aurrekora"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Aldatu tamaina"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Gorde"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Ez gorde"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Baliteke aplikazioak ez funtzionatzea pantaila zatituan."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikazioak ez du onartzen pantaila zatitua"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Baliteke aplikazioak ez funtzionatzea bigarren mailako pantailetan."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Burbuila"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Kudeatu"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Baztertu da globoa."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Saka ezazu aplikazioa berrabiarazteko, eta ezarri pantaila osoko modua."</string>
+ <string name="got_it" msgid="4428750913636945527">"Ados"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index a41811d..ca7d077e 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"رد شدن به بعدی"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"رد شدن به قبلی"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"تغییر اندازه"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"مخفیسازی"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"لغو مخفیسازی"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ممکن است برنامه با «صفحهٔ دونیمه» کار نکند."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"برنامه از تقسیم صفحه پشتیبانی نمیکند."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ممکن است برنامه در نمایشگر ثانویه کار نکند."</string>
@@ -62,11 +64,13 @@
<string name="bubbles_user_education_title" msgid="2112319053732691899">"گپ بااستفاده از حبابکها"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"مکالمههای جدید بهصورت نمادهای شناور یا حبابکها نشان داده میشوند. برای باز کردن حبابکها ضربه بزنید. برای جابهجایی، آن را بکشید."</string>
<string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"کنترل حبابکها در هرزمانی"</string>
- <string name="bubbles_user_education_manage" msgid="3460756219946517198">"برای خاموش کردن «حبابکها» از این برنامه، روی «مدیریت» ضربه بزنید"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"برای خاموش کردن حبابکها از این برنامه، روی «مدیریت» ضربه بزنید"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"متوجهام"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"هیچ حبابک جدیدی وجود ندارد"</string>
- <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"حبابکها اخیر و حبابکها ردشده اینجا ظاهر خواهند شد"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"حبابکهای اخیر و حبابکهای ردشده اینجا ظاهر خواهند شد"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"حباب"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"مدیریت"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"حبابک رد شد."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"برای بازراهاندازی این برنامه و تغییر به حالت تمامصفحه، ضربه بزنید."</string>
+ <string name="got_it" msgid="4428750913636945527">"متوجهام"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
index fcdc70f..5f87163 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Siirry seuraavaan"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Siirry edelliseen"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Muuta kokoa"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Lisää turvasäilytykseen"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Poista turvasäilytyksestä"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Sovellus ei ehkä toimi jaetulla näytöllä."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Sovellus ei tue jaetun näytön tilaa."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Sovellus ei ehkä toimi toissijaisella näytöllä."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Kupla"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Ylläpidä"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Kupla ohitettu."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Napauta, niin sovellus käynnistyy uudelleen ja siirtyy koko näytön tilaan."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
index ed82237..68df591 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Passer au suivant"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Revenir au précédent"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionner"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Ajouter à la réserve"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Retirer de la réserve"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Il est possible que l\'application ne fonctionne pas en mode Écran partagé."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"L\'application n\'est pas compatible avec l\'écran partagé."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Il est possible que l\'application ne fonctionne pas sur un écran secondaire."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bulle"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gérer"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulle ignorée."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Touchez pour redémarrer cette application et passer en plein écran."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
index ad98b85..eecc9cb 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Passer au contenu suivant"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Passer au contenu précédent"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionner"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Il est possible que l\'application ne fonctionne pas en mode Écran partagé."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Application incompatible avec l\'écran partagé."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Il est possible que l\'application ne fonctionne pas sur un écran secondaire."</string>
@@ -61,7 +63,7 @@
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ne pas afficher la conversation dans une bulle"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatter en utilisant des bulles"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Les nouvelles conversations s\'affichent sous forme d\'icônes flottantes ou bulles. Appuyez sur la bulle pour l\'ouvrir. Faites-la glisser pour la déplacer."</string>
- <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Contrôler les paramètres des bulles"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Contrôlez les bulles à tout moment"</string>
<string name="bubbles_user_education_manage" msgid="3460756219946517198">"Appuyez sur \"Gérer\" pour désactiver les bulles de cette application"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Aucune bulle récente"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bulle"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gérer"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulle fermée."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Appuyez pour redémarrer cette application et activer le mode plein écran."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
index 529825e..3583caf 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Ir ao seguinte"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Ir ao anterior"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Cambiar tamaño"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Esconder"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Non esconder"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Pode que a aplicación non funcione coa pantalla dividida."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"A aplicación non é compatible coa función de pantalla dividida."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"É posible que a aplicación non funcione nunha pantalla secundaria."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Burbulla"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Xestionar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ignorouse a burbulla."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Toca o botón para reiniciar esta aplicación e abrila en pantalla completa."</string>
+ <string name="got_it" msgid="4428750913636945527">"Entendido"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
index ee23e1e9..e0654bd 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"આગલા પર જાઓ"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"પહેલાંના પર જાઓ"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"કદ બદલો"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"છુપાવો"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"બતાવો"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"વિભાજિત-સ્ક્રીન સાથે ઍપ કદાચ કામ ન કરે."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ઍપ્લિકેશન સ્ક્રીન-વિભાજનનું સમર્થન કરતી નથી."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ઍપ્લિકેશન ગૌણ ડિસ્પ્લે પર કદાચ કામ ન કરે."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"બબલ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"મેનેજ કરો"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"બબલ છોડી દેવાયો."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"આ ઍપ ફરીથી ચાલુ કરવા માટે ટૅપ કરીને પૂર્ણ સ્ક્રીન કરો."</string>
+ <string name="got_it" msgid="4428750913636945527">"સમજાઈ ગયું"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index 34c1c85..55a30f2 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"अगले पर जाएं"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"पिछले पर जाएं"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"आकार बदलें"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"छिपाएं"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"दिखाएं"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ऐप्लिकेशन शायद स्प्लिट स्क्रीन मोड में काम न करे."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ऐप विभाजित स्क्रीन का समर्थन नहीं करता है."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"हो सकता है कि ऐप प्राइमरी (मुख्य) डिस्प्ले के अलावा बाकी दूसरे डिस्प्ले पर काम न करे."</string>
@@ -67,6 +69,8 @@
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"हाल ही के बबल्स मौजूद नहीं हैं"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"हाल ही के बबल्स और हटाए गए बबल्स यहां दिखेंगे"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
- <string name="manage_bubbles_text" msgid="7730624269650594419">"प्रबंधित करें"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"मैनेज करें"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल खारिज किया गया."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"इस ऐप्लिकेशन को रीस्टार्ट करने और फ़ुल स्क्रीन पर देखने के लिए टैप करें."</string>
+ <string name="got_it" msgid="4428750913636945527">"ठीक है"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
index 32b21aa..f6acb5c 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Preskoči na sljedeće"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Preskoči na prethodno"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Promjena veličine"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Sakrijte"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Poništite sakrivanje"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacija možda neće funkcionirati s podijeljenim zaslonom."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacija ne podržava podijeljeni zaslon."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija možda neće funkcionirati na sekundarnom zaslonu."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljanje"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić odbačen."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Dodirnite da biste ponovo pokrenuli tu aplikaciju i prikazali je na cijelom zaslonu."</string>
+ <string name="got_it" msgid="4428750913636945527">"Shvaćam"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
index 123b127..0c1c8a40 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Ugrás a következőre"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Ugrás az előzőre"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Átméretezés"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Félretevés"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Félretevés megszüntetése"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Lehet, hogy az alkalmazás nem működik osztott képernyős nézetben."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Az alkalmazás nem támogatja az osztott képernyős nézetet."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Előfordulhat, hogy az alkalmazás nem működik másodlagos kijelzőn."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Buborék"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Kezelés"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Buborék elvetve."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Koppintson az alkalmazás újraindításához és a teljes képernyős mód elindításához."</string>
+ <string name="got_it" msgid="4428750913636945527">"Rendben"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
index b047cf1..36204c1 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Անցնել հաջորդին"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Վերադառնալ նախորդին"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Փոխել չափը"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Թաքցնել"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Ցուցադրել"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Հավելվածը չի կարող աշխատել տրոհված էկրանի ռեժիմում։"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Հավելվածը չի աջակցում էկրանի տրոհումը:"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Հավելվածը կարող է չաշխատել լրացուցիչ էկրանի վրա"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Պղպջակ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Կառավարել"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ամպիկը փակվեց։"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Հպեք՝ հավելվածը վերագործարկելու և լիաէկրան ռեժիմին անցնելու համար։"</string>
+ <string name="got_it" msgid="4428750913636945527">"Եղավ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
index a75cdb4..de962c4 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Lewati ke berikutnya"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Lewati ke sebelumnya"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ubah ukuran"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Batalkan stash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikasi mungkin tidak berfungsi dengan layar terpisah."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App tidak mendukung layar terpisah."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikasi mungkin tidak berfungsi pada layar sekunder."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Balon"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Kelola"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balon ditutup."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Ketuk untuk memulai ulang aplikasi ini dan membuka layar penuh."</string>
+ <string name="got_it" msgid="4428750913636945527">"Oke"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
index 3b28148..c205d22 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Fara á næsta"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Fara á fyrra"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Breyta stærð"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Geymsla"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Taka úr geymslu"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Hugsanlega virkar forritið ekki með skjáskiptingu."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Forritið styður ekki að skjánum sé skipt."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Hugsanlegt er að forritið virki ekki á öðrum skjá."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Blaðra"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Stjórna"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Blöðru lokað."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Ýttu til að endurræsa forritið og sýna það á öllum skjánum."</string>
+ <string name="got_it" msgid="4428750913636945527">"Ég skil"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
index 8a2b9db..c788a03 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Passa ai contenuti successivi"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Passa ai contenuti precedenti"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ridimensiona"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Accantona"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Annulla accantonamento"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"L\'app potrebbe non funzionare con lo schermo diviso."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"L\'app non supporta la modalità Schermo diviso."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"L\'app potrebbe non funzionare su un display secondario."</string>
@@ -60,7 +62,7 @@
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignora bolla"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Non mettere la conversazione nella bolla"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatta utilizzando le bolle"</string>
- <string name="bubbles_user_education_description" msgid="4215862563054175407">"Le nuove conversazioni vengono visualizzate come icone mobili o bolle. Tocca per aprire la bolla. Trascinala per spostarla."</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Le nuove conversazioni vengono mostrate come icone mobili o bolle. Tocca per aprire la bolla. Trascinala per spostarla."</string>
<string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Controlla le bolle quando vuoi"</string>
<string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tocca Gestisci per disattivare le bolle dall\'app"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Fumetto"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gestisci"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Fumetto ignorato."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Tocca per riavviare l\'app e passare alla modalità a schermo intero."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
index 20114a7..b0c03ed 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -18,16 +18,18 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="pip_phone_close" msgid="5783752637260411309">"סגירה"</string>
- <string name="pip_phone_expand" msgid="2579292903468287504">"הרחב"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"הרחבה"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"הגדרות"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"תפריט"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> במצב תמונה בתוך תמונה"</string>
- <string name="pip_notification_message" msgid="8854051911700302620">"אם אינך רוצה שהתכונה הזו תשמש את <xliff:g id="NAME">%s</xliff:g>, יש להקיש כדי לפתוח את ההגדרות ולכבות את התכונה."</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"אם אינך רוצה שהתכונה הזו תשמש את <xliff:g id="NAME">%s</xliff:g>, יש להקיש כדי לפתוח את ההגדרות ולהשבית את התכונה."</string>
<string name="pip_play" msgid="3496151081459417097">"הפעלה"</string>
- <string name="pip_pause" msgid="690688849510295232">"השהה"</string>
+ <string name="pip_pause" msgid="690688849510295232">"השהיה"</string>
<string name="pip_skip_to_next" msgid="8403429188794867653">"אפשר לדלג אל הבא"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"אפשר לדלג אל הקודם"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"שינוי גודל"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"הסתרה זמנית"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ביטול ההסתרה הזמנית"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ייתכן שהאפליקציה לא תפעל במסך מפוצל."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"האפליקציה אינה תומכת במסך מפוצל."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ייתכן שהאפליקציה לא תפעל במסך משני."</string>
@@ -41,14 +43,14 @@
<string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"מסך עליון מלא"</string>
<string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"עליון 70%"</string>
<string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"עליון 50%"</string>
- <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"עליון 30%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"למעלה 30%"</string>
<string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"מסך תחתון מלא"</string>
- <string name="one_handed_tutorial_title" msgid="4583241688067426350">"איך להשתמש במצב שימוש ביד אחת"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"איך להשתמש בתכונה \'מצב שימוש ביד אחת\'"</string>
<string name="one_handed_tutorial_description" msgid="3486582858591353067">"כדי לצאת, יש להחליק למעלה מתחתית המסך או להקיש במקום כלשהו במסך מעל האפליקציה"</string>
<string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"הפעלה של מצב שימוש ביד אחת"</string>
<string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"יציאה ממצב שימוש ביד אחת"</string>
- <string name="bubbles_settings_button_description" msgid="1301286017420516912">"הגדרות בשביל בועות של <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"גלישה"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"הגדרות לבועות של <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"אפשרויות נוספות"</string>
<string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"הוספה בחזרה לערימה"</string>
<string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> מהאפליקציה <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> מ-<xliff:g id="APP_NAME">%2$s</xliff:g> ועוד <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"בועה"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ניהול"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"הבועה נסגרה."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"צריך להקיש כדי להפעיל מחדש את האפליקציה הזו ולעבור למסך מלא."</string>
+ <string name="got_it" msgid="4428750913636945527">"הבנתי"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings_tv.xml b/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
index 8ca54e0..ef98a9c 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
@@ -19,6 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"תמונה בתוך תמונה"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(תוכנית ללא כותרת)"</string>
- <string name="pip_close" msgid="9135220303720555525">"סגור PIP"</string>
+ <string name="pip_close" msgid="9135220303720555525">"סגירת PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"מסך מלא"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index fbb2951..36700bd 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"次へスキップ"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"前へスキップ"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"サイズ変更"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"非表示"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"表示"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"アプリは分割画面では動作しないことがあります。"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"アプリで分割画面がサポートされていません。"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"アプリはセカンダリ ディスプレイでは動作しないことがあります。"</string>
@@ -61,7 +63,7 @@
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"会話をバブルで表示しない"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"チャットでバブルを使う"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"新しい会話はフローティング アイコン(バブル)として表示されます。タップするとバブルが開きます。ドラッグしてバブルを移動できます。"</string>
- <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"いつでもバブルを管理"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"バブルはいつでも管理可能"</string>
<string name="bubbles_user_education_manage" msgid="3460756219946517198">"このアプリからのバブルを OFF にするには、[管理] をタップしてください"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"最近閉じたバブルはありません"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"バブル"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ふきだしが非表示になっています。"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"タップしてこのアプリを再起動すると、全画面表示になります。"</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
index f978481..af1377a 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"შემდეგზე გადასვლა"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"წინაზე გადასვლა"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ზომის შეცვლა"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"გადანახვა"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"გადანახვის გაუქმება"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"აპმა შეიძლება არ იმუშაოს გაყოფილი ეკრანის რეჟიმში."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ეკრანის გაყოფა არ არის მხარდაჭერილი აპის მიერ."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"აპმა შეიძლება არ იმუშაოს მეორეულ ეკრანზე."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ბუშტი"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"მართვა"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ბუშტი დაიხურა."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"შეეხეთ ამ აპის გადასატვირთად და გადადით სრულ ეკრანზე."</string>
+ <string name="got_it" msgid="4428750913636945527">"გასაგებია"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
index 2d27faf..6deb0b8 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Келесіге өту"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Алдыңғысына оралу"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Өлшемін өзгерту"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Жасыру"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Көрсету"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Қолданба экранды бөлу режимінде жұмыс істемеуі мүмкін."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Қодланба бөлінген экранды қолдамайды."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Қолданба қосымша дисплейде жұмыс істемеуі мүмкін."</string>
@@ -68,5 +70,7 @@
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Соңғы және жабылған қалқыма хабарлар осы жерде көрсетіледі."</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Көпіршік"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Басқару"</string>
- <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Қалқымалы анықтама өшірілді."</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Қалқыма хабар жабылды."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Бұл қолданбаны қайта қосып, толық экранға өту үшін түртіңіз."</string>
+ <string name="got_it" msgid="4428750913636945527">"Түсінікті"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
index d503b7a..c59d0fc 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"រំលងទៅបន្ទាប់"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"រំលងទៅក្រោយ"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ប្ដូរទំហំ"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"លាក់ជាបណ្ដោះអាសន្ន"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ឈប់លាក់ជាបណ្ដោះអាសន្ន"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"កម្មវិធីអាចនឹងមិនដំណើរការជាមួយមុខងារបំបែកអេក្រង់ទេ។"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"កម្មវិធីមិនគាំទ្រអេក្រង់បំបែកជាពីរទេ"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"កម្មវិធីនេះប្រហែលជាមិនដំណើរការនៅលើអេក្រង់បន្ទាប់បន្សំទេ។"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ពពុះ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"គ្រប់គ្រង"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"បានច្រានចោលសារលេចឡើង។"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"ចុចដើម្បីចាប់ផ្ដើមកម្មវិធីនេះឡើងវិញ រួចចូលប្រើពេញអេក្រង់។"</string>
+ <string name="got_it" msgid="4428750913636945527">"យល់ហើយ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
index 3d61d84..5e655b4 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"ಮುಂದಕ್ಕೆ ಸ್ಕಿಪ್ ಮಾಡಿ"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"ಹಿಂದಕ್ಕೆ ಸ್ಕಿಪ್ ಮಾಡಿ"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ಮರುಗಾತ್ರಗೊಳಿಸಿ"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"ಸ್ಟ್ಯಾಶ್ ಮಾಡಿ"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ಅನ್ಸ್ಟ್ಯಾಶ್ ಮಾಡಿ"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ವಿಭಜಿಸಿದ ಸ್ಕ್ರೀನ್ನಲ್ಲಿ ಆ್ಯಪ್ ಕೆಲಸ ಮಾಡದೇ ಇರಬಹುದು."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ಅಪ್ಲಿಕೇಶನ್ ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಬೆಂಬಲಿಸುವುದಿಲ್ಲ."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ಸೆಕೆಂಡರಿ ಡಿಸ್ಪ್ಲೇಗಳಲ್ಲಿ ಅಪ್ಲಿಕೇಶನ್ ಕಾರ್ಯ ನಿರ್ವಹಿಸದೇ ಇರಬಹುದು."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ಬಬಲ್"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ನಿರ್ವಹಿಸಿ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ಬಬಲ್ ವಜಾಗೊಳಿಸಲಾಗಿದೆ."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"ಈ ಆ್ಯಪ್ ಅನ್ನು ಮರುಪ್ರಾರಂಭಿಸಲು ಮತ್ತು ಪೂರ್ಣ ಸ್ಕ್ರೀನ್ನಲ್ಲಿ ನೋಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
+ <string name="got_it" msgid="4428750913636945527">"ಅರ್ಥವಾಯಿತು"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
index ea7ad56..af34ef4 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"다음으로 건너뛰기"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"이전으로 건너뛰기"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"크기 조절"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"숨기기"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"숨기기 취소"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"앱이 분할 화면에서 작동하지 않을 수 있습니다."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"앱이 화면 분할을 지원하지 않습니다."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"앱이 보조 디스플레이에서 작동하지 않을 수도 있습니다."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"버블"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"관리"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"대화창을 닫았습니다."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"탭하여 이 앱을 다시 시작하고 전체 화면으로 이동합니다."</string>
+ <string name="got_it" msgid="4428750913636945527">"확인"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index 611b2d6..c16041d 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Кийинкисине өткөрүп жиберүү"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Мурункусуна өткөрүп жиберүү"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Өлчөмүн өзгөртүү"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Сейфке салуу"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Сейфтен чыгаруу"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Колдонмодо экран бөлүнбөшү мүмкүн."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Колдонмодо экран бөлүнбөйт."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Колдонмо кошумча экранда иштебей коюшу мүмкүн."</string>
@@ -62,11 +64,13 @@
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Калкып чыкма билдирмелер аркылуу маектешүү"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Жаңы жазышуулар калкыма сүрөтчөлөр же калкып чыкма билдирмелер түрүндө көрүнөт. Калкып чыкма билдирмелерди ачуу үчүн таптап коюңуз. Жылдыруу үчүн сүйрөңүз."</string>
<string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Калкып чыкма билдирмелерди каалаган убакта көзөмөлдөңүз"</string>
- <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Бул колдонмодогу калкып чыкма билдирмелерди өчүрүү үчүн, \"Башкарууну\" басыңыз"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Бул колдонмодогу калкып чыкма билдирмелерди өчүрүү үчүн \"Башкарууну\" басыңыз"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Түшүндүм"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Азырынча эч нерсе жок"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Акыркы жана жабылган калкып чыкма билдирмелер ушул жерде көрүнөт"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Көбүк"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Башкаруу"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Калкып чыкма билдирме жабылды."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Бул колдонмону өчүрүп күйгүзүп, толук экранга өтүү үчүн таптап коюңуз."</string>
+ <string name="got_it" msgid="4428750913636945527">"Түшүндүм"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
index a1c998c..a578b0a 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"ຂ້າມໄປລາຍການໜ້າ"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"ຂ້າມໄປລາຍການກ່ອນນີ້"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ປ່ຽນຂະໜາດ"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"ເກັບໄວ້ບ່ອນເກັບສ່ວນຕົວ"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ເອົາອອກຈາກບ່ອນເກັບສ່ວນຕົວ"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ແອັບອາດໃຊ້ບໍ່ໄດ້ກັບການແບ່ງໜ້າຈໍ."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ແອັບບໍ່ຮອງຮັບໜ້າຈໍແບບແຍກກັນ."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ແອັບອາດບໍ່ສາມາດໃຊ້ໄດ້ໃນໜ້າຈໍທີສອງ."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ຟອງ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ຈັດການ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ປິດ Bubble ໄສ້ແລ້ວ."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"ແຕະເພື່ອຣີສະຕາດແອັບນີ້ ແລະ ໃຊ້ແບບເຕັມຈໍ."</string>
+ <string name="got_it" msgid="4428750913636945527">"ເຂົ້າໃຈແລ້ວ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
index b2ccd57..e037839 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Praleisti ir eiti į kitą"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Praleisti ir eiti į ankstesnį"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Pakeisti dydį"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Paslėpti"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Nebeslėpti"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Programa gali neveikti naudojant išskaidyto ekrano režimą."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Programoje nepalaikomas skaidytas ekranas."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Programa gali neveikti antriniame ekrane."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Debesėlis"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Tvarkyti"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Debesėlio atsisakyta."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Palieskite, kad paleistumėte iš naujo šią programą ir įjungtumėte viso ekrano režimą."</string>
+ <string name="got_it" msgid="4428750913636945527">"Supratau"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
index e6d0c77..05472e4 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Pāriet uz nākamo"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Pāriet uz iepriekšējo"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Mainīt lielumu"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Paslēpt"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Rādīt"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Iespējams, lietotne nedarbosies ekrāna sadalīšanas režīmā."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Lietotnē netiek atbalstīta ekrāna sadalīšana."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Lietotne, iespējams, nedarbosies sekundārajā displejā."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Burbulis"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Pārvaldīt"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Burbulis ir noraidīts."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Pieskarieties, lai restartētu šo lietotni un pārietu pilnekrāna režīmā."</string>
+ <string name="got_it" msgid="4428750913636945527">"Labi"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
index 43f2881..9cb2c69 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Прескокни до следната"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Прескокни до претходната"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Промени големина"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Сокријте"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Прикажете"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Апликацијата може да не работи со поделен екран."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Апликацијата не поддржува поделен екран."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Апликацијата може да не функционира на друг екран."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Балонче"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Управувајте"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Балончето е отфрлено."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Допрете за да ја рестартирате апликацијава и да ја отворите на цел екран."</string>
+ <string name="got_it" msgid="4428750913636945527">"Сфатив"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
index e675861..f0bf513 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"അടുത്തതിലേക്ക് പോകുക"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"മുമ്പത്തേതിലേക്ക് പോകുക"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"വലുപ്പം മാറ്റുക"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"സ്റ്റാഷ് ചെയ്യൽ"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"അൺസ്റ്റാഷ് ചെയ്യൽ"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"സ്ക്രീൻ വിഭജന മോഡിൽ ആപ്പ് പ്രവർത്തിച്ചേക്കില്ല."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"സ്പ്ലിറ്റ്-സ്ക്രീനിനെ ആപ്പ് പിന്തുണയ്ക്കുന്നില്ല."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"രണ്ടാം ഡിസ്പ്ലേയിൽ ആപ്പ് പ്രവർത്തിച്ചേക്കില്ല."</string>
@@ -66,7 +68,9 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"മനസ്സിലായി"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"അടുത്തിടെയുള്ള ബബിളുകൾ ഒന്നുമില്ല"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"അടുത്തിടെയുള്ള ബബിളുകൾ, ഡിസ്മിസ് ചെയ്ത ബബിളുകൾ എന്നിവ ഇവിടെ ദൃശ്യമാവും"</string>
- <string name="notification_bubble_title" msgid="6082910224488253378">"ബബ്ൾ"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"ബബിൾ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"മാനേജ് ചെയ്യുക"</string>
- <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ബബ്ൾ ഡിസ്മിസ് ചെയ്തു."</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ബബിൾ ഡിസ്മിസ് ചെയ്തു."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"ഈ ആപ്പ് റീസ്റ്റാർട്ട് ചെയ്ത് പൂർണ്ണ സ്ക്രീനിലേക്ക് മാറാൻ ടാപ്പ് ചെയ്യുക."</string>
+ <string name="got_it" msgid="4428750913636945527">"മനസ്സിലായി"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index 044fd9f..68822cb 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Дараагийн медиад очих"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Өмнөх медиад очих"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Хэмжээг өөрчлөх"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Нуух"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Ил гаргах"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Апп хуваагдсан дэлгэц дээр ажиллахгүй байж болзошгүй."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Энэ апп нь дэлгэц хуваах тохиргоог дэмждэггүй."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Апп хоёрдогч дэлгэцэд ажиллахгүй."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Бөмбөлөг"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Удирдах"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Бөмбөлгийг үл хэрэгссэн."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Энэ аппыг дахин эхлүүлж, бүтэн дэлгэцэд орохын тулд товшино уу."</string>
+ <string name="got_it" msgid="4428750913636945527">"Ойлголоо"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index e838cf5..a4b7be4 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"डावलून पुढे जा"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"डावलून मागे जा"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"आकार बदला"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"स्टॅश करा"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"अनस्टॅश करा"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"अॅप कदाचित स्प्लिट स्क्रीनसह काम करू शकत नाही."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"अॅप स्क्रीन-विभाजनास समर्थन देत नाही."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"दुसऱ्या डिस्प्लेवर अॅप कदाचित चालणार नाही."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"व्यवस्थापित करा"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल डिसमिस केला."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"हे अॅप रीस्टार्ट करण्यासाठी आणि फुल स्क्रीन करण्यासाठी टॅप करा."</string>
+ <string name="got_it" msgid="4428750913636945527">"समजले"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index 6664f38..2f33bfa 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Langkau ke seterusnya"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Langkau ke sebelumnya"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ubah saiz"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Sembunyikan"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Tunjukkan"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Apl mungkin tidak berfungsi dengan skrin pisah."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Apl tidak menyokong skrin pisah."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Apl mungkin tidak berfungsi pada paparan kedua."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Gelembung"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Urus"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Gelembung diketepikan."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Ketik untuk memulakan semula apl ini dan menggunakan skrin penuh."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
index 9681d14..018ffc0 100644
--- a/libs/WindowManager/Shell/res/values-my/strings.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"နောက်တစ်ခုသို့ ကျော်ရန်"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"ယခင်တစ်ခုသို့ ပြန်သွားရန်"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"အရွယ်အစားပြောင်းရန်"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"သိုဝှက်ရန်"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"မသိုဝှက်ရန်"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"မျက်နှာပြင် ခွဲ၍ပြသခြင်းဖြင့် အက်ပ်သည် အလုပ်မလုပ်ပါ။"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"အက်ပ်သည် မျက်နှာပြင်ခွဲပြရန် ပံ့ပိုးထားခြင်းမရှိပါ။"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ဤအက်ပ်အနေဖြင့် ဒုတိယဖန်သားပြင်ပေါ်တွင် အလုပ်လုပ်မည် မဟုတ်ပါ။"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ပူဖောင်းဖောက်သံ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"စီမံရန်"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ပူဖောင်းကွက် ဖယ်လိုက်သည်။"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"ဤအက်ပ်ကို ပြန်စပြီး ဖန်သားပြင်အပြည့်လုပ်ရန် တို့ပါ။"</string>
+ <string name="got_it" msgid="4428750913636945527">"ရပြီ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
index 986e890..a23ad90 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Hopp til neste"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Hopp til forrige"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Endre størrelse"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Oppbevar"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Avslutt oppbevaring"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Det kan hende at appen ikke fungerer med delt skjerm."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Appen støtter ikke delt skjerm."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Appen fungerer kanskje ikke på en sekundær skjerm."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Boble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Administrer"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Boblen er avvist."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Trykk for å starte denne appen på nytt og vise den i fullskjerm."</string>
+ <string name="got_it" msgid="4428750913636945527">"Greit"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index 0369c6d..5b9b872 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -28,9 +28,11 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"अर्कोमा जानुहोस्"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"अघिल्लोमा जानुहोस्"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"आकार बदल्नुहोस्"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"स्ट्यास गर्नुहोस्"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"अनस्ट्यास गर्नुहोस्"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"एप विभाजित स्क्रिनमा काम नगर्न सक्छ।"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"अनुप्रयोगले विभाजित-स्क्रिनलाई समर्थन गर्दैन।"</string>
- <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"यो अनुप्रयोगले सहायक प्रदर्शनमा काम नगर्नसक्छ।"</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"यो एपले सहायक प्रदर्शनमा काम नगर्नसक्छ।"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"अनुप्रयोगले सहायक प्रदर्शनहरूमा लञ्च सुविधालाई समर्थन गर्दैन।"</string>
<string name="accessibility_divider" msgid="703810061635792791">"विभाजित-स्क्रिन छुट्याउने"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"बायाँ भाग फुल स्क्रिन"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"व्यवस्थापन गर्नुहोस्"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल हटाइयो।"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"यो एप रिस्टार्ट गर्न ट्याप गर्नुहोस् र फुल स्क्रिन मोडमा जानुहोस्।"</string>
+ <string name="got_it" msgid="4428750913636945527">"बुझेँ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
index 26c276e..69fc25a 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -22,12 +22,14 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Instellingen"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> is in scherm-in-scherm"</string>
- <string name="pip_notification_message" msgid="8854051911700302620">"Als je niet wilt dat <xliff:g id="NAME">%s</xliff:g> deze functie gebruikt, tik je om de instellingen te openen en schakel je de functie uit."</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Als je niet wilt dat <xliff:g id="NAME">%s</xliff:g> deze functie gebruikt, tik je om de instellingen te openen en zet je de functie uit."</string>
<string name="pip_play" msgid="3496151081459417097">"Afspelen"</string>
<string name="pip_pause" msgid="690688849510295232">"Onderbreken"</string>
<string name="pip_skip_to_next" msgid="8403429188794867653">"Doorgaan naar volgende"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Teruggaan naar vorige"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Formaat aanpassen"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Verbergen"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Niet meer verbergen"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"De app werkt mogelijk niet met gesplitst scherm."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App biedt geen ondersteuning voor gesplitst scherm."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App werkt mogelijk niet op een secundair scherm."</string>
@@ -58,15 +60,17 @@
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Naar rechtsonder verplaatsen"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Instellingen voor <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Bubbel sluiten"</string>
- <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Gesprekken niet in bubbels weergeven"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Gesprekken niet in bubbels tonen"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatten met bubbels"</string>
- <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nieuwe gesprekken worden weergegeven als zwevende iconen of \'bubbels\'. Tik om een bubbel te openen. Sleep om de bubbel te verplaatsen."</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nieuwe gesprekken worden als zwevende iconen of bubbels getoond. Tik om een bubbel te openen. Sleep om een bubbel te verplaatsen."</string>
<string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Beheer bubbels wanneer je wilt"</string>
- <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tik op Beheren om bubbels van deze app uit te schakelen"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tik op Beheren om bubbels van deze app uit te zetten"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Geen recente bubbels"</string>
- <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recente bubbels en gesloten bubbels worden hier weergegeven"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recente bubbels en gesloten bubbels zie je hier"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubbel"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Beheren"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubbel gesloten."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Tik om deze app opnieuw te starten en te openen op het volledige scherm."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
index 27f1622..ac1e84a 100644
--- a/libs/WindowManager/Shell/res/values-or/strings.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"ପରବର୍ତ୍ତୀକୁ ଯାଆନ୍ତୁ"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"ପୂର୍ବବର୍ତ୍ତୀକୁ ଛାଡ଼ନ୍ତୁ"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ରିସାଇଜ୍ କରନ୍ତୁ"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"ଲୁଚାନ୍ତୁ"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ଦେଖାନ୍ତୁ"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ସ୍ପ୍ଲିଟ୍-ସ୍କ୍ରିନରେ ଆପ୍ କାମ କରିନପାରେ।"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ଆପ୍ ସ୍ପ୍ଲିଟ୍-ସ୍କ୍ରୀନକୁ ସପୋର୍ଟ କରେ ନାହିଁ।"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ସେକେଣ୍ଡାରୀ ଡିସପ୍ଲେରେ ଆପ୍ କାମ ନକରିପାରେ।"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ବବଲ୍"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ପରିଚାଳନା କରନ୍ତୁ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ବବଲ୍ ଖାରଜ କରାଯାଇଛି।"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"ଏହି ଆପକୁ ରିଷ୍ଟାର୍ଟ କରି ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ।"</string>
+ <string name="got_it" msgid="4428750913636945527">"ବୁଝିଗଲି"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
index 96688b9..bf5b733c 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"ਅਗਲੇ \'ਤੇ ਜਾਓ"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"ਪਿਛਲੇ \'ਤੇ ਜਾਓ"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ਆਕਾਰ ਬਦਲੋ"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"ਸਟੈਸ਼"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ਅਣਸਟੈਸ਼"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਐਪ ਸਪਲਿਟ-ਸਕ੍ਰੀਨ ਨਾਲ ਕੰਮ ਨਾ ਕਰੇ।"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ਐਪ ਸਪਲਿਟ-ਸਕ੍ਰੀਨ ਨੂੰ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦੀ।"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਐਪ ਸੈਕੰਡਰੀ ਡਿਸਪਲੇ \'ਤੇ ਕੰਮ ਨਾ ਕਰੇ।"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ਬੁਲਬੁਲਾ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ਬਬਲ ਨੂੰ ਖਾਰਜ ਕੀਤਾ ਗਿਆ।"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"ਇਸ ਐਪ ਨੂੰ ਮੁੜ-ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ ਅਤੇ ਪੂਰੀ ਸਕ੍ਰੀਨ ਮੋਡ \'ਤੇ ਜਾਓ।"</string>
+ <string name="got_it" msgid="4428750913636945527">"ਸਮਝ ਲਿਆ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
index 6b640b5..cd659ba 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Dalej"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Wstecz"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Zmień rozmiar"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Przenieś do schowka"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Zabierz ze schowka"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacja może nie działać przy podzielonym ekranie."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacja nie obsługuje dzielonego ekranu."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacja może nie działać na dodatkowym ekranie."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Dymek"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Zarządzaj"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Zamknięto dymek"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Kliknij, by uruchomić tę aplikację ponownie i przejść w tryb pełnoekranowy."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
index 465d2d1..9c97ffe 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Pular para a próxima"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Pular para a anterior"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionar"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Ocultar"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Exibir"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"É possível que o app não funcione com a tela dividida."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"O app não é compatível com a divisão de tela."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"É possível que o app não funcione em uma tela secundária."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bolha"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gerenciar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão dispensado."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Toque para reiniciar o app e usar tela cheia."</string>
+ <string name="got_it" msgid="4428750913636945527">"Ok"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
index df841bf..1f5b0ab 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Mudar para o seguinte"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Mudar para o anterior"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionar"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Armazenar"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Remover do armazenamento"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"A app pode não funcionar com o ecrã dividido."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"A app não é compatível com o ecrã dividido."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"A app pode não funcionar num ecrã secundário."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Balão"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gerir"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão ignorado."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Toque para reiniciar esta app e ficar em ecrã inteiro."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
index 465d2d1..9c97ffe 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Pular para a próxima"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Pular para a anterior"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionar"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Ocultar"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Exibir"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"É possível que o app não funcione com a tela dividida."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"O app não é compatível com a divisão de tela."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"É possível que o app não funcione em uma tela secundária."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bolha"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gerenciar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão dispensado."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Toque para reiniciar o app e usar tela cheia."</string>
+ <string name="got_it" msgid="4428750913636945527">"Ok"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
index 55a4376..d694be1 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Treceți la următorul"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Treceți la cel anterior"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionați"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stocați"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Anulați stocarea"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Este posibil ca aplicația să nu funcționeze cu ecranul împărțit."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplicația nu acceptă ecranul împărțit."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Este posibil ca aplicația să nu funcționeze pe un ecran secundar."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Balon"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionați"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balonul a fost respins."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Atingeți ca să reporniți aplicația și să treceți în modul ecran complet."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
index 8ae00d2..e9bfffb 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Перейти к следующему"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Перейти к предыдущему"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Изменить размер"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Скрыть"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Показать"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"В режиме разделения экрана приложение может работать нестабильно."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Приложение не поддерживает разделение экрана."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Приложение может не работать на дополнительном экране"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Всплывающая подсказка"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Настроить"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Всплывающий чат закрыт."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Нажмите, чтобы перезапустить приложение и перейти в полноэкранный режим."</string>
+ <string name="got_it" msgid="4428750913636945527">"ОК"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
index 081926f..ba178f0 100644
--- a/libs/WindowManager/Shell/res/values-si/strings.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"ඊළඟ එකට පනින්න"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"පෙර එකට පනින්න"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ප්රතිප්රමාණ කරන්න"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"සඟවා තබන්න"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"සඟවා තැබීම ඉවත් කරන්න"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"යෙදුම බෙදුම් තිරය සමග ක්රියා නොකළ හැකිය"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"යෙදුම බෙදුණු-තිරය සඳහා සහාය නොදක්වයි."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"යෙදුම ද්විතියික සංදර්ශකයක ක්රියා නොකළ හැකිය."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"බුබුළු"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"කළමනා කරන්න"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"බුබුල ඉවත දමා ඇත."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"මෙම යෙදුම යළි ඇරඹීමට සහ පූර්ණ තිරයට යාමට තට්ටු කරන්න."</string>
+ <string name="got_it" msgid="4428750913636945527">"තේරුණා"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
index 24fded7..e048ca1 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Preskočiť na ďalšie"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Preskočiť na predchádzajúce"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Zmeniť veľkosť"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Skryť"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Zrušiť skrytie"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikácia nemusí fungovať s rozdelenou obrazovkou."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikácia nepodporuje rozdelenú obrazovku."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikácia nemusí fungovať na sekundárnej obrazovke."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bublina"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Spravovať"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bublina bola zavretá."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Klepnutím reštartujete túto aplikáciu a prejdete do režimu celej obrazovky."</string>
+ <string name="got_it" msgid="4428750913636945527">"Dobre"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
index 3f42530..ed05908 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Preskoči na naslednjega"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Preskoči na prejšnjega"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Spremeni velikost"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Zakrij"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Razkrij"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacija morda ne deluje v načinu razdeljenega zaslona."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacija ne podpira načina razdeljenega zaslona."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija morda ne bo delovala na sekundarnem zaslonu."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Mehurček"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljanje"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblaček je bil opuščen."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Dotaknite se za vnovični zagon te aplikacije in preklop v celozaslonski način."</string>
+ <string name="got_it" msgid="4428750913636945527">"Razumem"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
index ddae724..13e830c 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Kalo te tjetra"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Kalo tek e mëparshmja"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ndrysho përmasat"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Fshih"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Mos e fshih"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacioni mund të mos funksionojë me ekranin e ndarë."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacioni nuk mbështet ekranin e ndarë."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacioni mund të mos funksionojë në një ekran dytësor."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Flluskë"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Menaxho"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Flluska u hoq."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Trokit për ta rinisur këtë aplikacion dhe për të kaluar në ekranin e plotë."</string>
+ <string name="got_it" msgid="4428750913636945527">"E kuptova"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
index 74c9ac0..be6857b 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Пређи на следеће"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Пређи на претходно"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Промените величину"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Ставите у тајну меморију"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Уклоните из тајне меморије"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Апликација можда неће радити са подељеним екраном."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Апликација не подржава подељени екран."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Апликација можда неће функционисати на секундарном екрану."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Облачић"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Управљајте"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Облачић је одбачен."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Додирните да бисте рестартовали апликацију и прешли у режим целог екрана."</string>
+ <string name="got_it" msgid="4428750913636945527">"Важи"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
index 81328a8..e61e69b 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Hoppa till nästa"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Hoppa till föregående"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ändra storlek"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Utför stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Återställ stash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Appen kanske inte fungerar med delad skärm."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Appen har inte stöd för delad skärm."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Appen kanske inte fungerar på en sekundär skärm."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubbla"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Hantera"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubblan ignorerades."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Tryck för att starta om appen i helskärmsläge."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml
index 4559832..476af11 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Ruka ufikie inayofuata"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Ruka ufikie iliyotangulia"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Badilisha ukubwa"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Ficha"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Fichua"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Huenda programu isifanye kazi kwenye skrini inayogawanywa."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Programu haiwezi kutumia skrini iliyogawanywa."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Huenda programu isifanye kazi kwenye dirisha lingine."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Kiputo"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Dhibiti"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Umeondoa kiputo."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Gusa ili uzime na uwashe programu hii, kisha nenda kwenye skrini nzima."</string>
+ <string name="got_it" msgid="4428750913636945527">"Nimeelewa"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
index 586ee94..bc27389 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"அடுத்ததற்குச் செல்"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"முந்தையதற்குச் செல்"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"அளவு மாற்று"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"திரைப் பிரிப்பு அம்சத்தில் ஆப்ஸ் செயல்படாமல் போகக்கூடும்."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"திரையைப் பிரிப்பதைப் ஆப்ஸ் ஆதரிக்கவில்லை."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"இரண்டாம்நிலைத் திரையில் ஆப்ஸ் வேலை செய்யாமல் போகக்கூடும்."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"பபிள்"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"நிர்வகி"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"குமிழ் நிராகரிக்கப்பட்டது."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"தட்டுவதன் மூலம் இந்த ஆப்ஸை மீண்டும் தொடங்கலாம், முழுத்திரையில் பார்க்கலாம்."</string>
+ <string name="got_it" msgid="4428750913636945527">"சரி"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index 4e85b43..624b8b3 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"దాటవేసి తర్వాత దానికి వెళ్లు"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"దాటవేసి మునుపటి దానికి వెళ్లు"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"పరిమాణం మార్చు"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"స్టాచ్"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ఆన్స్టాచ్"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"స్క్రీన్ విభజనతో యాప్ పని చేయకపోవచ్చు."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"అనువర్తనంలో స్క్రీన్ విభజనకు మద్దతు లేదు."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ప్రత్యామ్నాయ డిస్ప్లేలో యాప్ పని చేయకపోవచ్చు."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"బబుల్"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"మేనేజ్ చేయండి"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"బబుల్ విస్మరించబడింది."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"ఈ యాప్ను రీస్టార్ట్ చేయడానికి ట్యాప్ చేసి, ఆపై పూర్తి స్క్రీన్లోకి వెళ్లండి."</string>
+ <string name="got_it" msgid="4428750913636945527">"అర్థమైంది"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index 66c7018..9017b3f 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"ข้ามไปรายการถัดไป"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"ข้ามไปรายการก่อนหน้า"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ปรับขนาด"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"เก็บเข้าที่เก็บส่วนตัว"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"เอาออกจากที่เก็บส่วนตัว"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"แอปอาจใช้ไม่ได้กับโหมดแบ่งหน้าจอ"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"แอปไม่สนับสนุนการแยกหน้าจอ"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"แอปอาจไม่ทำงานในจอแสดงผลรอง"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"บับเบิล"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"จัดการ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ปิดบับเบิลแล้ว"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"แตะเพื่อรีสตาร์ทแอปนี้และแสดงแบบเต็มหน้าจอ"</string>
+ <string name="got_it" msgid="4428750913636945527">"รับทราบ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index a76bf6f..c484caf 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Lumaktaw sa susunod"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Lumaktaw sa nakaraan"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"I-resize"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"I-stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"I-unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Posibleng hindi gumana ang app sa split screen."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Hindi sinusuportahan ng app ang split-screen."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Maaaring hindi gumana ang app sa pangalawang display."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Pamahalaan"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Na-dismiss na ang bubble."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"I-tap para i-restart ang app na ito at mag-full screen."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
index b3276da..ca856a1 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Sonrakine atla"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Öncekine atla"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Yeniden boyutlandır"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Depola"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Depolama"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Uygulama bölünmüş ekranda çalışmayabilir."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Uygulama bölünmüş ekranı desteklemiyor."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Uygulama ikincil ekranda çalışmayabilir."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Baloncuk"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Yönet"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balon kapatıldı."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Bu uygulamayı yeniden başlatmak ve tam ekrana geçmek için dokunun."</string>
+ <string name="got_it" msgid="4428750913636945527">"Anladım"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml
index 8e303cf..08e8d29 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Перейти далі"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Перейти назад"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Змінити розмір"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Сховати"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Показати"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Додаток може не працювати в режимі розділеного екрана."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Додаток не підтримує розділення екрана."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Додаток може не працювати на додатковому екрані."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Спливаюче сповіщення"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Налаштувати"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Спливаюче сповіщення закрито."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Натисніть, щоб перезапустити додаток і перейти в повноекранний режим."</string>
+ <string name="got_it" msgid="4428750913636945527">"Зрозуміло"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
index 4b0adc6..06c0927 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"نظرانداز کرکے اگلے پر جائیں"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"نظرانداز کرکے پچھلے پر جائیں"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"سائز تبدیل کریں"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ممکن ہے کہ ایپ اسپلٹ اسکرین کے ساتھ کام نہ کرے۔"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ایپ سپلٹ اسکرین کو سپورٹ نہیں کرتی۔"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ممکن ہے ایپ ثانوی ڈسپلے پر کام نہ کرے۔"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"بلبلہ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"نظم کریں"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"بلبلہ برخاست کر دیا گیا۔"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"یہ ایپ دوبارہ شروع کرنے کے لیے تھپتھپائیں اور پوری اسکرین پر جائیں۔"</string>
+ <string name="got_it" msgid="4428750913636945527">"سمجھ آ گئی"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
index 74b135d..6a873a3 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Keyingisiga o‘tish"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Avvalgisiga qaytish"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Oʻlchamini oʻzgartirish"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Berkitish"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Chiqarish"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Bu ilova ekranni ikkiga ajratish rejimini dastaklamaydi."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Bu ilova ekranni bo‘lish xususiyatini qo‘llab-quvvatlamaydi."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Bu ilova qo‘shimcha ekranda ishlamasligi mumkin."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Pufaklar"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Boshqarish"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulutcha yopildi."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Bu ilovani qaytadan ishga tushirish va butun ekranda ochish uchun bosing."</string>
+ <string name="got_it" msgid="4428750913636945527">"OK"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
index ce37231..4d4eebc 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Chuyển tới mục tiếp theo"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Chuyển về mục trước"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Đổi kích thước"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Ẩn"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Hiện"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Ứng dụng có thể không hoạt động với tính năng chia đôi màn hình."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Ứng dụng không hỗ trợ chia đôi màn hình."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Ứng dụng có thể không hoạt động trên màn hình phụ."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bong bóng"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Quản lý"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Đã đóng bong bóng."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Nhấn để khởi động lại ứng dụng này và xem ở chế độ toàn màn hình."</string>
+ <string name="got_it" msgid="4428750913636945527">"Tôi hiểu"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
index 3143130..3b8c889 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"跳到下一个"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"跳到上一个"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"调整大小"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"隐藏"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"取消隐藏"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"应用可能无法在分屏模式下正常运行。"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"应用不支持分屏。"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"应用可能无法在辅显示屏上正常运行。"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"气泡"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"已关闭对话泡。"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"点按即可重启此应用并进入全屏模式。"</string>
+ <string name="got_it" msgid="4428750913636945527">"知道了"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
index 4f8bfe0..9ba82b5 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"跳到下一個"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"跳到上一個"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"調整大小"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"保護"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"取消保護"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"應用程式可能無法在分割畫面中運作。"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"應用程式不支援分割畫面。"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"應用程式可能無法在次要顯示屏上運作。"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"氣泡"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"對話氣泡已關閉。"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"輕按即可重新開啟此應用程式並放大至全螢幕。"</string>
+ <string name="got_it" msgid="4428750913636945527">"知道了"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
index 6fb8ed9..0af8d24 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"跳到下一個"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"跳到上一個"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"調整大小"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"暫時隱藏"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"取消暫時隱藏"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"應用程式可能無法在分割畫面中運作。"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"這個應用程式不支援分割畫面。"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"應用程式可能無法在次要顯示器上運作。"</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"泡泡"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"已關閉泡泡。"</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"輕觸即可重新啟動這個應用程式並進入全螢幕模式。"</string>
+ <string name="got_it" msgid="4428750913636945527">"我知道了"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
index cab2776..c8199c8 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Yeqela kokulandelayo"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Yeqela kokwangaphambilini"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Shintsha usayizi"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Yenza isiteshi"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Susa isiteshi"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Izinhlelo zokusebenza kungenzeka zingasebenzi ngesikrini esihlukanisiwe."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Uhlelo lokusebenza alusekeli isikrini esihlukanisiwe."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Uhlelo lokusebenza kungenzeka lungasebenzi kusibonisi sesibili."</string>
@@ -69,4 +71,6 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Ibhamuza"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Phatha"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ibhamuza licashisiwe."</string>
+ <string name="restart_button_description" msgid="5887656107651190519">"Thepha ukuze uqale kabusha lolu hlelo lokusebenza uphinde uye kusikrini esigcwele."</string>
+ <string name="got_it" msgid="4428750913636945527">"Ngiyezwa"</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 1861e48..2f3214d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
@@ -40,6 +40,8 @@
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
import java.io.PrintWriter;
import java.util.concurrent.Executor;
@@ -74,6 +76,7 @@
private final ShellTaskOrganizer mTaskOrganizer;
private final Executor mShellExecutor;
+ private final SyncTransactionQueue mSyncQueue;
private ActivityManager.RunningTaskInfo mTaskInfo;
private WindowContainerToken mTaskToken;
@@ -89,11 +92,12 @@
private final Rect mTmpRootRect = new Rect();
private final int[] mTmpLocation = new int[2];
- public TaskView(Context context, ShellTaskOrganizer organizer) {
+ public TaskView(Context context, ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue) {
super(context, null, 0, 0, true /* disableBackgroundLayer */);
mTaskOrganizer = organizer;
mShellExecutor = organizer.getExecutor();
+ mSyncQueue = syncQueue;
setUseAlpha();
getHolder().addCallback(this);
mGuard.open("release");
@@ -189,8 +193,7 @@
WindowContainerTransaction wct = new WindowContainerTransaction();
wct.setBounds(mTaskToken, mTmpRect);
- // TODO(b/151449487): Enable synchronization
- mTaskOrganizer.applyTransaction(wct);
+ mSyncQueue.queue(wct);
}
/**
@@ -236,14 +239,16 @@
private void updateTaskVisibility() {
WindowContainerTransaction wct = new WindowContainerTransaction();
wct.setHidden(mTaskToken, !mSurfaceCreated /* hidden */);
- mTaskOrganizer.applyTransaction(wct);
- // TODO(b/151449487): Only call callback once we enable synchronization
- if (mListener != null) {
- final int taskId = mTaskInfo.taskId;
+ mSyncQueue.queue(wct);
+ if (mListener == null) {
+ return;
+ }
+ int taskId = mTaskInfo.taskId;
+ mSyncQueue.runInSync((t) -> {
mListenerExecutor.execute(() -> {
mListener.onTaskVisibilityChanged(taskId, mSurfaceCreated);
});
- }
+ });
}
@Override
@@ -264,10 +269,12 @@
updateTaskVisibility();
}
mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, true);
- // TODO: Synchronize show with the resize
onLocationChanged();
if (taskInfo.taskDescription != null) {
- setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
+ int backgroundColor = taskInfo.taskDescription.getBackgroundColor();
+ mSyncQueue.runInSync((t) -> {
+ setResizeBackgroundColor(t, backgroundColor);
+ });
}
if (mListener != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java
index 58ca1fb..8286d10 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java
@@ -20,8 +20,8 @@
import android.content.Context;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.annotations.ExternalThread;
-import com.android.wm.shell.common.annotations.ShellMainThread;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -30,12 +30,14 @@
public class TaskViewFactoryController {
private final ShellTaskOrganizer mTaskOrganizer;
private final ShellExecutor mShellExecutor;
+ private final SyncTransactionQueue mSyncQueue;
private final TaskViewFactory mImpl = new TaskViewFactoryImpl();
public TaskViewFactoryController(ShellTaskOrganizer taskOrganizer,
- ShellExecutor shellExecutor) {
+ ShellExecutor shellExecutor, SyncTransactionQueue syncQueue) {
mTaskOrganizer = taskOrganizer;
mShellExecutor = shellExecutor;
+ mSyncQueue = syncQueue;
}
public TaskViewFactory asTaskViewFactory() {
@@ -44,7 +46,7 @@
/** Creates an {@link TaskView} */
public void create(@UiContext Context context, Executor executor, Consumer<TaskView> onCreate) {
- TaskView taskView = new TaskView(context, mTaskOrganizer);
+ TaskView taskView = new TaskView(context, mTaskOrganizer, mSyncQueue);
executor.execute(() -> {
onCreate.accept(taskView);
});
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 8c8a56a..086fa3b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -85,6 +85,7 @@
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
@@ -136,6 +137,7 @@
private final TaskStackListenerImpl mTaskStackListener;
private final ShellTaskOrganizer mTaskOrganizer;
private final DisplayController mDisplayController;
+ private final SyncTransactionQueue mSyncQueue;
// Used to post to main UI thread
private final ShellExecutor mMainExecutor;
@@ -208,7 +210,8 @@
ShellTaskOrganizer organizer,
DisplayController displayController,
ShellExecutor mainExecutor,
- Handler mainHandler) {
+ Handler mainHandler,
+ SyncTransactionQueue syncQueue) {
BubbleLogger logger = new BubbleLogger(uiEventLogger);
BubblePositioner positioner = new BubblePositioner(context, windowManager);
BubbleData data = new BubbleData(context, logger, positioner, mainExecutor);
@@ -216,7 +219,7 @@
new BubbleDataRepository(context, launcherApps, mainExecutor),
statusBarService, windowManager, windowManagerShellWrapper, launcherApps,
logger, taskStackListener, organizer, positioner, displayController, mainExecutor,
- mainHandler);
+ mainHandler, syncQueue);
}
/**
@@ -238,7 +241,8 @@
BubblePositioner positioner,
DisplayController displayController,
ShellExecutor mainExecutor,
- Handler mainHandler) {
+ Handler mainHandler,
+ SyncTransactionQueue syncQueue) {
mContext = context;
mLauncherApps = launcherApps;
mBarService = statusBarService == null
@@ -261,6 +265,7 @@
mSavedBubbleKeysPerUser = new SparseSetArray<>();
mBubbleIconFactory = new BubbleIconFactory(context);
mDisplayController = displayController;
+ mSyncQueue = syncQueue;
}
public void initialize() {
@@ -544,6 +549,10 @@
return mTaskOrganizer;
}
+ SyncTransactionQueue getSyncTransactionQueue() {
+ return mSyncQueue;
+ }
+
/** Contains information to help position things on the screen. */
BubblePositioner getPositioner() {
return mBubblePositioner;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index f81f086..9018bf4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -317,7 +317,8 @@
bringChildToFront(mOverflowView);
mManageButton.setVisibility(GONE);
} else {
- mTaskView = new TaskView(mContext, mController.getTaskOrganizer());
+ mTaskView = new TaskView(mContext, mController.getTaskOrganizer(),
+ mController.getSyncTransactionQueue());
mTaskView.setListener(mController.getMainExecutor(), mTaskViewListener);
mExpandedViewContainer.addView(mTaskView);
bringChildToFront(mTaskView);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index e42f511..d844dd8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -92,12 +92,16 @@
private DividerSnapAlgorithm mDividerSnapAlgorithm;
private int mDividePosition;
private boolean mInitialized = false;
+ private int mOrientation;
+ private int mRotation;
public SplitLayout(String windowName, Context context, Configuration configuration,
SplitLayoutHandler splitLayoutHandler,
SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks,
DisplayImeController displayImeController, ShellTaskOrganizer taskOrganizer) {
mContext = context.createConfigurationContext(configuration);
+ mOrientation = configuration.orientation;
+ mRotation = configuration.windowConfiguration.getRotation();
mSplitLayoutHandler = splitLayoutHandler;
mDisplayImeController = displayImeController;
mSplitWindowManager = new SplitWindowManager(
@@ -144,25 +148,36 @@
/** Applies new configuration, returns {@code false} if there's no effect to the layout. */
public boolean updateConfiguration(Configuration configuration) {
+ boolean affectsLayout = false;
+
+ // Make sure to render the divider bar with proper resources that matching the screen
+ // orientation.
+ final int orientation = configuration.orientation;
+ if (orientation != mOrientation) {
+ mOrientation = orientation;
+ mContext = mContext.createConfigurationContext(configuration);
+ mSplitWindowManager.setConfiguration(configuration);
+ affectsLayout = true;
+ }
+
+ // Update the split bounds when necessary. Besides root bounds changed, split bounds need to
+ // be updated when the rotation changed to cover the case that users rotated the screen 180
+ // degrees.
+ final int rotation = configuration.windowConfiguration.getRotation();
final Rect rootBounds = configuration.windowConfiguration.getBounds();
- if (mRootBounds.equals(rootBounds)) {
- return false;
+ if (rotation != mRotation || !mRootBounds.equals(rootBounds)) {
+ mRootBounds.set(rootBounds);
+ mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
+ resetDividerPosition();
+ affectsLayout = true;
}
- mContext = mContext.createConfigurationContext(configuration);
- mSplitWindowManager.setConfiguration(configuration);
- mRootBounds.set(rootBounds);
- mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
- resetDividerPosition();
-
- // Don't inflate divider bar if it is not initialized.
- if (!mInitialized) {
- return false;
+ if (mInitialized) {
+ release();
+ init();
}
- release();
- init();
- return true;
+ return affectsLayout;
}
/** Updates recording bounds of divider window and both of the splits. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java
index d9409ec..b1fa2ac 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java
@@ -204,7 +204,8 @@
@Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
if (transition != mPendingDismiss && transition != mPendingEnter) {
// If we're not in split-mode, just abort
@@ -239,12 +240,12 @@
if (change.getParent() != null) {
// This is probably reparented, so we want the parent to be immediately visible
final TransitionInfo.Change parentChange = info.getChange(change.getParent());
- t.show(parentChange.getLeash());
- t.setAlpha(parentChange.getLeash(), 1.f);
+ startTransaction.show(parentChange.getLeash());
+ startTransaction.setAlpha(parentChange.getLeash(), 1.f);
// and then animate this layer outside the parent (since, for example, this is
// the home task animating from fullscreen to part-screen).
- t.reparent(leash, info.getRootLeash());
- t.setLayer(leash, info.getChanges().size() - i);
+ startTransaction.reparent(leash, info.getRootLeash());
+ startTransaction.setLayer(leash, info.getChanges().size() - i);
// build the finish reparent/reposition
mFinishTransaction.reparent(leash, parentChange.getLeash());
mFinishTransaction.setPosition(leash,
@@ -271,12 +272,12 @@
if (transition == mPendingEnter
&& mListener.mPrimary.token.equals(change.getContainer())
|| mListener.mSecondary.token.equals(change.getContainer())) {
- t.setWindowCrop(leash, change.getStartAbsBounds().width(),
+ startTransaction.setWindowCrop(leash, change.getStartAbsBounds().width(),
change.getStartAbsBounds().height());
if (mListener.mPrimary.token.equals(change.getContainer())) {
// Move layer to top since we want it above the oversized home task during
// animation even though home task is on top in hierarchy.
- t.setLayer(leash, info.getChanges().size() + 1);
+ startTransaction.setLayer(leash, info.getChanges().size() + 1);
}
}
boolean isOpening = Transitions.isOpeningType(info.getType());
@@ -289,7 +290,7 @@
// Dismissing via snap-to-top/bottom means that the dismissed task is already
// not-visible (usually cropped to oblivion) so immediately set its alpha to 0
// and don't animate it so it doesn't pop-in when reparented.
- t.setAlpha(leash, 0.f);
+ startTransaction.setAlpha(leash, 0.f);
} else {
startExampleAnimation(leash, false /* show */);
}
@@ -311,7 +312,7 @@
}
mSplitScreen.finishEnterSplitTransition(homeIsVisible);
}
- t.apply();
+ startTransaction.apply();
onFinish();
return true;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 6451b94..27707a4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -107,38 +107,6 @@
*/
private static final int ONE_SHOT_ALPHA_ANIMATION_TIMEOUT_MS = 1000;
- // Not a complete set of states but serves what we want right now.
- private enum State {
- UNDEFINED(0),
- TASK_APPEARED(1),
- ENTRY_SCHEDULED(2),
- ENTERING_PIP(3),
- ENTERED_PIP(4),
- EXITING_PIP(5);
-
- private final int mStateValue;
-
- State(int value) {
- mStateValue = value;
- }
-
- private boolean isInPip() {
- return mStateValue >= TASK_APPEARED.mStateValue
- && mStateValue != EXITING_PIP.mStateValue;
- }
-
- /**
- * Resize request can be initiated in other component, ignore if we are no longer in PIP,
- * still waiting for animation or we're exiting from it.
- *
- * @return {@code true} if the resize request should be blocked/ignored.
- */
- private boolean shouldBlockResizeRequest() {
- return mStateValue < ENTERING_PIP.mStateValue
- || mStateValue == EXITING_PIP.mStateValue;
- }
- }
-
private final Context mContext;
private final SyncTransactionQueue mSyncTransactionQueue;
private final PipBoundsState mPipBoundsState;
@@ -190,7 +158,8 @@
}
final boolean isExitPipDirection = isOutPipDirection(direction)
|| isRemovePipDirection(direction);
- if (mState != State.EXITING_PIP || isExitPipDirection) {
+ if (mPipTransitionState.getTransitionState() != PipTransitionState.EXITING_PIP
+ || isExitPipDirection) {
// Finish resize as long as we're not exiting PIP, or, if we are, only if this is
// the end of an exit PIP animation.
// This is necessary in case there was a resize animation ongoing when exit PIP
@@ -233,7 +202,7 @@
private ActivityManager.RunningTaskInfo mDeferredTaskInfo;
private WindowContainerToken mToken;
private SurfaceControl mLeash;
- private State mState = State.UNDEFINED;
+ private PipTransitionState mPipTransitionState;
private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
private long mLastOneShotAlphaAnimationTime;
private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
@@ -278,6 +247,7 @@
public PipTaskOrganizer(Context context,
@NonNull SyncTransactionQueue syncTransactionQueue,
+ @NonNull PipTransitionState pipTransitionState,
@NonNull PipBoundsState pipBoundsState,
@NonNull PipBoundsAlgorithm boundsHandler,
@NonNull PipMenuController pipMenuController,
@@ -291,6 +261,7 @@
@ShellMainThread ShellExecutor mainExecutor) {
mContext = context;
mSyncTransactionQueue = syncTransactionQueue;
+ mPipTransitionState = pipTransitionState;
mPipBoundsState = pipBoundsState;
mPipBoundsAlgorithm = boundsHandler;
mPipMenuController = pipMenuController;
@@ -326,14 +297,14 @@
}
public boolean isInPip() {
- return mState.isInPip();
+ return mPipTransitionState.isInPip();
}
/**
* Returns whether the entry animation is waiting to be started.
*/
public boolean isEntryScheduled() {
- return mState == State.ENTRY_SCHEDULED;
+ return mPipTransitionState.getTransitionState() == PipTransitionState.ENTRY_SCHEDULED;
}
/**
@@ -401,9 +372,11 @@
* @param animationDurationMs duration in millisecond for the exiting PiP transition
*/
public void exitPip(int animationDurationMs) {
- if (!mState.isInPip() || mState == State.EXITING_PIP || mToken == null) {
+ if (!mPipTransitionState.isInPip()
+ || mPipTransitionState.getTransitionState() == PipTransitionState.EXITING_PIP
+ || mToken == null) {
Log.wtf(TAG, "Not allowed to exitPip in current state"
- + " mState=" + mState + " mToken=" + mToken);
+ + " mState=" + mPipTransitionState.getTransitionState() + " mToken=" + mToken);
return;
}
@@ -427,7 +400,12 @@
wct.setBoundsChangeTransaction(mToken, tx);
// Set the exiting state first so if there is fixed rotation later, the running animation
// won't be interrupted by alpha animation for existing PiP.
- mState = State.EXITING_PIP;
+ mPipTransitionState.setTransitionState(PipTransitionState.EXITING_PIP);
+
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ mPipTransitionController.startTransition(destinationBounds, wct);
+ return;
+ }
mSyncTransactionQueue.queue(wct);
mSyncTransactionQueue.runInSync(t -> {
// Make sure to grab the latest source hint rect as it could have been
@@ -465,9 +443,9 @@
* Removes PiP immediately.
*/
public void removePip() {
- if (!mState.isInPip() || mToken == null) {
+ if (!mPipTransitionState.isInPip() || mToken == null) {
Log.wtf(TAG, "Not allowed to removePip in current state"
- + " mState=" + mState + " mToken=" + mToken);
+ + " mState=" + mPipTransitionState.getTransitionState() + " mToken=" + mToken);
return;
}
@@ -481,7 +459,7 @@
animator.setDuration(mExitAnimationDuration);
animator.setInterpolator(Interpolators.ALPHA_OUT);
animator.start();
- mState = State.EXITING_PIP;
+ mPipTransitionState.setTransitionState(PipTransitionState.EXITING_PIP);
}
private void removePipImmediately() {
@@ -503,7 +481,7 @@
Objects.requireNonNull(info, "Requires RunningTaskInfo");
mTaskInfo = info;
mToken = mTaskInfo.token;
- mState = State.TASK_APPEARED;
+ mPipTransitionState.setTransitionState(PipTransitionState.TASK_APPEARED);
mLeash = leash;
mPictureInPictureParams = mTaskInfo.pictureInPictureParams;
setBoundsStateForEntry(mTaskInfo.topActivity, mPictureInPictureParams,
@@ -557,7 +535,7 @@
scheduleAnimateResizePip(currentBounds, destinationBounds, 0 /* startingAngle */,
sourceHintRect, TRANSITION_DIRECTION_TO_PIP, mEnterAnimationDuration,
null /* updateBoundsCallback */);
- mState = State.ENTERING_PIP;
+ mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
} else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
enterPipWithAlphaAnimation(destinationBounds, mEnterAnimationDuration);
mOneShotAnimationType = ANIM_TYPE_BOUNDS;
@@ -584,7 +562,7 @@
final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
animateResizePip(currentBounds, destinationBounds, sourceHintRect,
TRANSITION_DIRECTION_TO_PIP, mEnterAnimationDuration, 0 /* startingAngle */);
- mState = State.ENTERING_PIP;
+ mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
}
/**
@@ -609,7 +587,7 @@
mSurfaceControlTransactionFactory.getTransaction();
tx.setAlpha(mLeash, 0f);
tx.apply();
- mState = State.ENTRY_SCHEDULED;
+ mPipTransitionState.setTransitionState(PipTransitionState.ENTRY_SCHEDULED);
applyEnterPipSyncTransaction(destinationBounds, () -> {
mPipAnimationController
.getAnimator(mTaskInfo, mLeash, destinationBounds, 0f, 1f)
@@ -620,7 +598,7 @@
.start();
// mState is set right after the animation is kicked off to block any resize
// requests such as offsetPip that may have been called prior to the transition.
- mState = State.ENTERING_PIP;
+ mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
}, null /* boundsChangeTransaction */);
}
@@ -667,7 +645,7 @@
private void sendOnPipTransitionStarted(
@PipAnimationController.TransitionDirection int direction) {
if (direction == TRANSITION_DIRECTION_TO_PIP) {
- mState = State.ENTERING_PIP;
+ mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
}
mPipTransitionController.sendOnPipTransitionStarted(direction);
}
@@ -676,7 +654,7 @@
void sendOnPipTransitionFinished(
@PipAnimationController.TransitionDirection int direction) {
if (direction == TRANSITION_DIRECTION_TO_PIP) {
- mState = State.ENTERED_PIP;
+ mPipTransitionState.setTransitionState(PipTransitionState.ENTERED_PIP);
}
mPipTransitionController.sendOnPipTransitionFinished(direction);
// Apply the deferred RunningTaskInfo if applicable after all proper callbacks are sent.
@@ -701,7 +679,7 @@
*/
@Override
public void onTaskVanished(ActivityManager.RunningTaskInfo info) {
- if (mState == State.UNDEFINED) {
+ if (mPipTransitionState.getTransitionState() == PipTransitionState.UNDEFINED) {
return;
}
final WindowContainerToken token = info.token;
@@ -713,7 +691,7 @@
clearWaitForFixedRotation();
mInSwipePipToHomeTransition = false;
mPictureInPictureParams = null;
- mState = State.UNDEFINED;
+ mPipTransitionState.setTransitionState(PipTransitionState.UNDEFINED);
// Re-set the PIP bounds to none.
mPipBoundsState.setBounds(new Rect());
mPipUiEventLoggerLogger.setTaskInfo(null);
@@ -727,8 +705,10 @@
@Override
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
Objects.requireNonNull(mToken, "onTaskInfoChanged requires valid existing mToken");
- if (mState != State.ENTERED_PIP && mState != State.EXITING_PIP) {
- Log.d(TAG, "Defer onTaskInfoChange in current state: " + mState);
+ if (mPipTransitionState.getTransitionState() != PipTransitionState.ENTERED_PIP
+ && mPipTransitionState.getTransitionState() != PipTransitionState.EXITING_PIP) {
+ Log.d(TAG, "Defer onTaskInfoChange in current state: "
+ + mPipTransitionState.getTransitionState());
// Defer applying PiP parameters if the task is entering PiP to avoid disturbing
// the animation.
mDeferredTaskInfo = info;
@@ -761,7 +741,7 @@
mNextRotation = newRotation;
mWaitForFixedRotation = true;
- if (mState.isInPip()) {
+ if (mPipTransitionState.isInPip()) {
// Fade out the existing PiP to avoid jump cut during seamless rotation.
fadeExistingPip(false /* show */);
}
@@ -772,7 +752,7 @@
if (!mWaitForFixedRotation) {
return;
}
- if (mState == State.TASK_APPEARED) {
+ if (mPipTransitionState.getTransitionState() == PipTransitionState.TASK_APPEARED) {
if (mInSwipePipToHomeTransition) {
onEndOfSwipePipToHomeTransition();
} else {
@@ -780,9 +760,11 @@
enterPipWithAlphaAnimation(mPipBoundsAlgorithm.getEntryDestinationBounds(),
mEnterAnimationDuration);
}
- } else if (mState == State.ENTERED_PIP && mHasFadeOut) {
+ } else if (mPipTransitionState.getTransitionState() == PipTransitionState.ENTERED_PIP
+ && mHasFadeOut) {
fadeExistingPip(true /* show */);
- } else if (mState == State.ENTERING_PIP && mDeferredAnimEndTransaction != null) {
+ } else if (mPipTransitionState.getTransitionState() == PipTransitionState.ENTERING_PIP
+ && mDeferredAnimEndTransaction != null) {
final PipAnimationController.PipTransitionAnimator<?> animator =
mPipAnimationController.getCurrentAnimator();
final Rect destinationBounds = animator.getDestinationBounds();
@@ -847,7 +829,7 @@
mPipAnimationController.getCurrentAnimator();
if (animator == null || !animator.isRunning()
|| animator.getTransitionDirection() != TRANSITION_DIRECTION_TO_PIP) {
- final boolean rotatingPip = mState.isInPip() && fromRotation;
+ final boolean rotatingPip = mPipTransitionState.isInPip() && fromRotation;
if (rotatingPip && mWaitForFixedRotation && mHasFadeOut) {
// The position will be used by fade-in animation when the fixed rotation is done.
mPipBoundsState.setBounds(destinationBoundsOut);
@@ -980,7 +962,7 @@
Rect currentBounds, Rect destinationBounds, float startingAngle, Rect sourceHintRect,
@PipAnimationController.TransitionDirection int direction, int durationMs,
Consumer<Rect> updateBoundsCallback) {
- if (!mState.isInPip()) {
+ if (!mPipTransitionState.isInPip()) {
// TODO: tend to use shouldBlockResizeRequest here as well but need to consider
// the fact that when in exitPip, scheduleAnimateResizePip is executed in the window
// container transaction callback and we want to set the mState immediately.
@@ -1010,7 +992,7 @@
final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
mSurfaceTransactionHelper
.crop(tx, mLeash, toBounds)
- .round(tx, mLeash, mState.isInPip());
+ .round(tx, mLeash, mPipTransitionState.isInPip());
if (mPipMenuController.isMenuVisible()) {
mPipMenuController.resizePipMenu(mLeash, tx, toBounds);
} else {
@@ -1088,7 +1070,7 @@
public void scheduleFinishResizePip(Rect destinationBounds,
@PipAnimationController.TransitionDirection int direction,
Consumer<Rect> updateBoundsCallback) {
- if (mState.shouldBlockResizeRequest()) {
+ if (mPipTransitionState.shouldBlockResizeRequest()) {
return;
}
@@ -1105,7 +1087,7 @@
mSurfaceTransactionHelper
.crop(tx, mLeash, destinationBounds)
.resetScale(tx, mLeash, destinationBounds)
- .round(tx, mLeash, mState.isInPip());
+ .round(tx, mLeash, mPipTransitionState.isInPip());
return tx;
}
@@ -1114,7 +1096,7 @@
*/
public void scheduleOffsetPip(Rect originalBounds, int offset, int duration,
Consumer<Rect> updateBoundsCallback) {
- if (mState.shouldBlockResizeRequest()) {
+ if (mPipTransitionState.shouldBlockResizeRequest()) {
return;
}
if (mWaitForFixedRotation) {
@@ -1372,7 +1354,7 @@
pw.println(innerPrefix + "mToken=" + mToken
+ " binder=" + (mToken != null ? mToken.asBinder() : null));
pw.println(innerPrefix + "mLeash=" + mLeash);
- pw.println(innerPrefix + "mState=" + mState);
+ pw.println(innerPrefix + "mState=" + mPipTransitionState.getTransitionState());
pw.println(innerPrefix + "mOneShotAnimationType=" + mOneShotAnimationType);
pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 4759550..93b0fcf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -18,6 +18,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
@@ -25,6 +26,7 @@
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection;
import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
import android.app.TaskInfo;
import android.content.Context;
@@ -35,6 +37,7 @@
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
import android.window.WindowContainerTransaction;
+import android.window.WindowContainerTransactionCallback;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -49,37 +52,77 @@
*/
public class PipTransition extends PipTransitionController {
+ private final PipTransitionState mPipTransitionState;
private final int mEnterExitAnimationDuration;
private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
private Transitions.TransitionFinishCallback mFinishCallback;
+ private Rect mExitDestinationBounds = new Rect();
public PipTransition(Context context,
- PipBoundsState pipBoundsState, PipMenuController pipMenuController,
+ PipBoundsState pipBoundsState,
+ PipTransitionState pipTransitionState,
+ PipMenuController pipMenuController,
PipBoundsAlgorithm pipBoundsAlgorithm,
PipAnimationController pipAnimationController,
Transitions transitions,
@NonNull ShellTaskOrganizer shellTaskOrganizer) {
super(pipBoundsState, pipMenuController, pipBoundsAlgorithm,
pipAnimationController, transitions, shellTaskOrganizer);
+ mPipTransitionState = pipTransitionState;
mEnterExitAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipResizeAnimationDuration);
}
@Override
+ public void startTransition(Rect destinationBounds, WindowContainerTransaction out) {
+ mExitDestinationBounds.set(destinationBounds);
+ mTransitions.startTransition(TRANSIT_EXIT_PIP, out, this);
+ }
+
+ @Override
public boolean startAnimation(@android.annotation.NonNull IBinder transition,
@android.annotation.NonNull TransitionInfo info,
- @android.annotation.NonNull SurfaceControl.Transaction t,
+ @android.annotation.NonNull SurfaceControl.Transaction startTransaction,
+ @android.annotation.NonNull SurfaceControl.Transaction finishTransaction,
@android.annotation.NonNull Transitions.TransitionFinishCallback finishCallback) {
+
+ if (info.getType() == TRANSIT_EXIT_PIP && info.getChanges().size() == 1) {
+ final TransitionInfo.Change change = info.getChanges().get(0);
+ mFinishCallback = finishCallback;
+ startTransaction.apply();
+ boolean success = startExpandAnimation(change.getTaskInfo(), change.getLeash(),
+ new Rect(mExitDestinationBounds));
+ mExitDestinationBounds.setEmpty();
+ return success;
+ }
+
+ // Search for an Enter PiP transition (along with a show wallpaper one)
+ TransitionInfo.Change enterPip = null;
+ TransitionInfo.Change wallpaper = null;
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
if (change.getTaskInfo() != null
&& change.getTaskInfo().configuration.windowConfiguration.getWindowingMode()
== WINDOWING_MODE_PINNED) {
- mFinishCallback = finishCallback;
- return startEnterAnimation(change.getTaskInfo(), change.getLeash(), t);
+ enterPip = change;
+ } else if ((change.getFlags() & FLAG_IS_WALLPAPER) != 0) {
+ wallpaper = change;
}
}
- return false;
+ if (enterPip == null) {
+ return false;
+ }
+
+ // Show the wallpaper if there is a wallpaper change.
+ if (wallpaper != null) {
+ startTransaction.show(wallpaper.getLeash());
+ startTransaction.setAlpha(wallpaper.getLeash(), 1.f);
+ }
+
+ mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
+ mFinishCallback = finishCallback;
+ return startEnterAnimation(enterPip.getTaskInfo(), enterPip.getLeash(),
+ startTransaction, finishTransaction);
}
@Nullable
@@ -93,20 +136,46 @@
public void onFinishResize(TaskInfo taskInfo, Rect destinationBounds,
@PipAnimationController.TransitionDirection int direction,
SurfaceControl.Transaction tx) {
+ if (isInPipDirection(direction)) {
+ mPipTransitionState.setTransitionState(PipTransitionState.ENTERED_PIP);
+ }
WindowContainerTransaction wct = new WindowContainerTransaction();
prepareFinishResizeTransaction(taskInfo, destinationBounds,
direction, tx, wct);
- mFinishCallback.onTransitionFinished(wct, null);
+ mFinishCallback.onTransitionFinished(wct, new WindowContainerTransactionCallback() {
+ @Override
+ public void onTransactionReady(int id, @NonNull SurfaceControl.Transaction t) {
+ t.merge(tx);
+ t.apply();
+ }
+ });
finishResizeForMenu(destinationBounds);
}
+ private boolean startExpandAnimation(final TaskInfo taskInfo, final SurfaceControl leash,
+ final Rect destinationBounds) {
+ PipAnimationController.PipTransitionAnimator animator =
+ mPipAnimationController.getAnimator(taskInfo, leash, mPipBoundsState.getBounds(),
+ mPipBoundsState.getBounds(), destinationBounds, null,
+ TRANSITION_DIRECTION_LEAVE_PIP, 0 /* startingAngle */, Surface.ROTATION_0);
+
+ animator.setTransitionDirection(TRANSITION_DIRECTION_LEAVE_PIP)
+ .setPipAnimationCallback(mPipAnimationCallback)
+ .setDuration(mEnterExitAnimationDuration)
+ .start();
+
+ return true;
+ }
+
private boolean startEnterAnimation(final TaskInfo taskInfo, final SurfaceControl leash,
- final SurfaceControl.Transaction t) {
+ final SurfaceControl.Transaction startTransaction,
+ final SurfaceControl.Transaction finishTransaction) {
setBoundsStateForEntry(taskInfo.topActivity, taskInfo.pictureInPictureParams,
taskInfo.topActivityInfo);
final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
final Rect currentBounds = taskInfo.configuration.windowConfiguration.getBounds();
PipAnimationController.PipTransitionAnimator animator;
+ finishTransaction.setPosition(leash, destinationBounds.left, destinationBounds.top);
if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
final Rect sourceHintRect =
PipBoundsAlgorithm.getValidSourceHintRect(
@@ -115,8 +184,7 @@
currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP,
0 /* startingAngle */, Surface.ROTATION_0);
} else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
- t.setAlpha(leash, 0f);
- t.apply();
+ startTransaction.setAlpha(leash, 0f);
animator = mPipAnimationController.getAnimator(taskInfo, leash, destinationBounds,
0f, 1f);
mOneShotAnimationType = ANIM_TYPE_BOUNDS;
@@ -124,6 +192,7 @@
throw new RuntimeException("Unrecognized animation type: "
+ mOneShotAnimationType);
}
+ startTransaction.apply();
animator.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
.setPipAnimationCallback(mPipAnimationCallback)
.setDuration(mEnterExitAnimationDuration)
@@ -158,6 +227,5 @@
}
wct.setBounds(taskInfo.token, taskBounds);
- wct.setBoundsChangeTransaction(taskInfo.token, tx);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index d801c91..bc26256 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -29,6 +29,7 @@
import android.os.Handler;
import android.os.Looper;
import android.view.SurfaceControl;
+import android.window.WindowContainerTransaction;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.transition.Transitions;
@@ -46,6 +47,7 @@
protected final PipBoundsState mPipBoundsState;
protected final ShellTaskOrganizer mShellTaskOrganizer;
protected final PipMenuController mPipMenuController;
+ protected final Transitions mTransitions;
private final Handler mMainHandler;
private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>();
@@ -98,6 +100,13 @@
SurfaceControl.Transaction tx) {
}
+ /**
+ * Called when the Shell wants to starts a transition/animation.
+ */
+ public void startTransition(Rect destinationBounds, WindowContainerTransaction out) {
+ // Default implementation does nothing.
+ }
+
public PipTransitionController(PipBoundsState pipBoundsState,
PipMenuController pipMenuController, PipBoundsAlgorithm pipBoundsAlgorithm,
PipAnimationController pipAnimationController, Transitions transitions,
@@ -107,6 +116,7 @@
mShellTaskOrganizer = shellTaskOrganizer;
mPipBoundsAlgorithm = pipBoundsAlgorithm;
mPipAnimationController = pipAnimationController;
+ mTransitions = transitions;
mMainHandler = new Handler(Looper.getMainLooper());
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
transitions.addHandler(this);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java
new file mode 100644
index 0000000..d23aada
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.pip;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Used to keep track of PiP leash state as it appears and animates by {@link PipTaskOrganizer} and
+ * {@link PipTransition}.
+ */
+public class PipTransitionState {
+
+ public static final int UNDEFINED = 0;
+ public static final int TASK_APPEARED = 1;
+ public static final int ENTRY_SCHEDULED = 2;
+ public static final int ENTERING_PIP = 3;
+ public static final int ENTERED_PIP = 4;
+ public static final int EXITING_PIP = 5;
+
+ // Not a complete set of states but serves what we want right now.
+ @IntDef(prefix = { "TRANSITION_STATE_" }, value = {
+ UNDEFINED,
+ TASK_APPEARED,
+ ENTRY_SCHEDULED,
+ ENTERING_PIP,
+ ENTERED_PIP,
+ EXITING_PIP
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TransitionState {}
+
+ private @TransitionState int mState;
+
+ public PipTransitionState() {
+ mState = UNDEFINED;
+ }
+
+ public void setTransitionState(@TransitionState int state) {
+ mState = state;
+ }
+
+ public @TransitionState int getTransitionState() {
+ return mState;
+ }
+
+ public boolean isInPip() {
+ return mState >= TASK_APPEARED
+ && mState != EXITING_PIP;
+ }
+
+ /**
+ * Resize request can be initiated in other component, ignore if we are no longer in PIP,
+ * still waiting for animation or we're exiting from it.
+ *
+ * @return {@code true} if the resize request should be blocked/ignored.
+ */
+ public boolean shouldBlockResizeRequest() {
+ return mState < ENTERING_PIP
+ || mState == EXITING_PIP;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
index b7caf72..551476d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
@@ -58,7 +58,8 @@
@Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @android.annotation.NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
return false;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index 002bfb6..d6afeba 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -64,6 +64,12 @@
return null;
}
+ /**
+ * Called when the keyguard occluded state changes.
+ * @param occluded Indicates if the keyguard is now occluded.
+ */
+ void onKeyguardOccludedChanged(boolean occluded);
+
/** Get a string representation of a stage type */
static String stageTypeToString(@StageType int stage) {
switch (stage) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 9a457b5..89e6ca8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -157,6 +157,10 @@
mStageCoordinator.exitSplitScreen();
}
+ public void onKeyguardOccludedChanged(boolean occluded) {
+ mStageCoordinator.onKeyguardOccludedChanged(occluded);
+ }
+
public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
mStageCoordinator.exitSplitScreenOnHide(exitSplitScreenOnHide);
}
@@ -284,6 +288,13 @@
mISplitScreen = new ISplitScreenImpl(SplitScreenController.this);
return mISplitScreen;
}
+
+ @Override
+ public void onKeyguardOccludedChanged(boolean occluded) {
+ mMainExecutor.execute(() -> {
+ SplitScreenController.this.onKeyguardOccludedChanged(occluded);
+ });
+ }
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index c37789e..69d0be6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -84,17 +84,19 @@
}
void playAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback,
@NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot) {
mFinishCallback = finishCallback;
mAnimatingTransition = transition;
if (mRemoteHandler != null) {
- mRemoteHandler.startAnimation(transition, info, t, mRemoteFinishCB);
+ mRemoteHandler.startAnimation(transition, info, startTransaction, finishTransaction,
+ mRemoteFinishCB);
mRemoteHandler = null;
return;
}
- playInternalAnimation(transition, info, t, mainRoot, sideRoot);
+ playInternalAnimation(transition, info, startTransaction, mainRoot, sideRoot);
}
private void playInternalAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 0264c5a..467f8c7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -44,6 +44,7 @@
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Rect;
+import android.hardware.devicestate.DeviceStateManager;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
@@ -115,7 +116,8 @@
private final List<SplitScreen.SplitScreenListener> mListeners = new ArrayList<>();
private final DisplayImeController mDisplayImeController;
private final SplitScreenTransitions mSplitTransitions;
- private boolean mExitSplitScreenOnHide = true;
+ private boolean mExitSplitScreenOnHide;
+ private boolean mKeyguardOccluded;
// TODO(b/187041611): remove this flag after totally deprecated legacy split
/** Whether the device is supporting legacy split or not. */
@@ -157,6 +159,10 @@
mSurfaceSession);
mDisplayImeController = displayImeController;
mRootTDAOrganizer.registerListener(displayId, this);
+ final DeviceStateManager deviceStateManager =
+ mContext.getSystemService(DeviceStateManager.class);
+ deviceStateManager.registerCallback(taskOrganizer.getExecutor(),
+ new DeviceStateManager.FoldStateListener(mContext, this::onFoldedStateChanged));
mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
mOnTransitionAnimationComplete);
transitions.addHandler(this);
@@ -253,17 +259,17 @@
}
void setSideStagePosition(@SplitPosition int sideStagePosition) {
- setSideStagePosition(sideStagePosition, true /* updateVisibility */);
+ setSideStagePosition(sideStagePosition, true /* updateBounds */);
}
private void setSideStagePosition(@SplitPosition int sideStagePosition,
- boolean updateVisibility) {
+ boolean updateBounds) {
if (mSideStagePosition == sideStagePosition) return;
mSideStagePosition = sideStagePosition;
sendOnStagePositionChanged();
- if (mSideStageListener.mVisible && updateVisibility) {
- onStageVisibilityChanged(mSideStageListener);
+ if (mSideStageListener.mVisible && updateBounds) {
+ onBoundsChanged(mSplitLayout);
}
}
@@ -275,6 +281,12 @@
mTaskOrganizer.applyTransaction(wct);
}
+ void onKeyguardOccludedChanged(boolean occluded) {
+ // Do not exit split directly, because it needs to wait for task info update to determine
+ // which task should remain on top after split dismissed.
+ mKeyguardOccluded = occluded;
+ }
+
void exitSplitScreen() {
exitSplitScreen(null /* childrenToTop */);
}
@@ -403,10 +415,20 @@
// Divider is only visible if both the main stage and side stages are visible
setDividerVisibility(isSplitScreenVisible());
- if (mExitSplitScreenOnHide && !mainStageVisible && !sideStageVisible) {
- // Exit split-screen if both stage are not visible.
- // TODO: This is only a temporary request from UX and is likely to be removed soon...
- exitSplitScreen();
+ if (!mainStageVisible && !sideStageVisible) {
+ if (mExitSplitScreenOnHide
+ // Don't dismiss staged split when both stages are not visible due to sleeping display,
+ // like the cases keyguard showing or screen off.
+ || (!mMainStage.mRootTaskInfo.isSleeping && !mSideStage.mRootTaskInfo.isSleeping)) {
+ exitSplitScreen();
+ }
+ } else if (mKeyguardOccluded) {
+ // At least one of the stages is visible while keyguard occluded. Dismiss split because
+ // there's show-when-locked activity showing on top of keyguard. Also make sure the
+ // task contains show-when-locked activity remains on top after split dismissed.
+ final StageTaskListener toTop =
+ mainStageVisible ? mMainStage : (sideStageVisible ? mSideStage : null);
+ exitSplitScreen(toTop);
}
if (mainStageVisible) {
@@ -580,11 +602,18 @@
public void onDisplayAreaInfoChanged(DisplayAreaInfo displayAreaInfo) {
mDisplayAreaInfo = displayAreaInfo;
if (mSplitLayout != null
- && mSplitLayout.updateConfiguration(mDisplayAreaInfo.configuration)) {
+ && mSplitLayout.updateConfiguration(mDisplayAreaInfo.configuration)
+ && mMainStage.isActive()) {
onBoundsChanged(mSplitLayout);
}
}
+ private void onFoldedStateChanged(boolean folded) {
+ if (folded && mMainStage.isActive()) {
+ exitSplitScreen(mMainStage);
+ }
+ }
+
private Rect getSideStageBounds() {
return mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT
? mSplitLayout.getBounds1() : mSplitLayout.getBounds2();
@@ -672,7 +701,8 @@
@Override
public boolean startAnimation(@NonNull IBinder transition,
@NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
if (transition != mSplitTransitions.mPendingDismiss
&& transition != mSplitTransitions.mPendingEnter) {
@@ -717,14 +747,14 @@
boolean shouldAnimate = true;
if (mSplitTransitions.mPendingEnter == transition) {
- shouldAnimate = startPendingEnterAnimation(transition, info, t);
+ shouldAnimate = startPendingEnterAnimation(transition, info, startTransaction);
} else if (mSplitTransitions.mPendingDismiss == transition) {
- shouldAnimate = startPendingDismissAnimation(transition, info, t);
+ shouldAnimate = startPendingDismissAnimation(transition, info, startTransaction);
}
if (!shouldAnimate) return false;
- mSplitTransitions.playAnimation(transition, info, t, finishCallback,
- mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token);
+ mSplitTransitions.playAnimation(transition, info, startTransaction, finishTransaction,
+ finishCallback, mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token);
return true;
}
@@ -754,7 +784,7 @@
// Update local states (before animating).
setDividerVisibility(true);
- setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, false /* updateVisibility */);
+ setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, false /* updateBounds */);
setSplitsVisible(true);
addDividerBarToTransition(info, t, true /* show */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index c6fb5af..4eadf8c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -16,6 +16,13 @@
package com.android.wm.shell.transition;
+import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
+import static android.app.ActivityOptions.ANIM_CUSTOM;
+import static android.app.ActivityOptions.ANIM_NONE;
+import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
+import static android.app.ActivityOptions.ANIM_SCALE_UP;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
@@ -29,17 +36,28 @@
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
+import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_CLOSE;
+import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_CLOSE;
+import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_OPEN;
+import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_NONE;
+import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_OPEN;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.graphics.Point;
import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
import android.os.IBinder;
+import android.os.SystemProperties;
+import android.os.UserHandle;
import android.util.ArrayMap;
import android.view.Choreographer;
import android.view.SurfaceControl;
+import android.view.SurfaceSession;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.Transformation;
@@ -61,30 +79,53 @@
public class DefaultTransitionHandler implements Transitions.TransitionHandler {
private static final int MAX_ANIMATION_DURATION = 3000;
+ /**
+ * Restrict ability of activities overriding transition animation in a way such that
+ * an activity can do it only when the transition happens within a same task.
+ *
+ * @see android.app.Activity#overridePendingTransition(int, int)
+ */
+ private static final String DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY =
+ "persist.wm.disable_custom_task_animation";
+
+ /**
+ * @see #DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY
+ */
+ static boolean sDisableCustomTaskAnimationProperty =
+ SystemProperties.getBoolean(DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY, true);
+
private final TransactionPool mTransactionPool;
+ private final Context mContext;
private final ShellExecutor mMainExecutor;
private final ShellExecutor mAnimExecutor;
private final TransitionAnimation mTransitionAnimation;
+ private final SurfaceSession mSurfaceSession = new SurfaceSession();
+
/** Keeps track of the currently-running animations associated with each transition. */
private final ArrayMap<IBinder, ArrayList<Animator>> mAnimations = new ArrayMap<>();
private final Rect mInsets = new Rect(0, 0, 0, 0);
private float mTransitionAnimationScaleSetting = 1.0f;
+ private final int mCurrentUserId;
+
DefaultTransitionHandler(@NonNull TransactionPool transactionPool, Context context,
@NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) {
mTransactionPool = transactionPool;
+ mContext = context;
mMainExecutor = mainExecutor;
mAnimExecutor = animExecutor;
mTransitionAnimation = new TransitionAnimation(context, false /* debug */, Transitions.TAG);
+ mCurrentUserId = UserHandle.myUserId();
AttributeCache.init(context);
}
@Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
"start default transition animation, info = %s", info);
@@ -100,16 +141,18 @@
mAnimations.remove(transition);
finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
};
+
+ final int wallpaperTransit = getWallpaperTransitType(info);
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
if (change.getMode() == TRANSIT_CHANGE) {
// No default animation for this, so just update bounds/position.
- t.setPosition(change.getLeash(),
+ startTransaction.setPosition(change.getLeash(),
change.getEndAbsBounds().left - change.getEndRelOffset().x,
change.getEndAbsBounds().top - change.getEndRelOffset().y);
if (change.getTaskInfo() != null) {
// Skip non-tasks since those usually have null bounds.
- t.setWindowCrop(change.getLeash(),
+ startTransaction.setWindowCrop(change.getLeash(),
change.getEndAbsBounds().width(), change.getEndAbsBounds().height());
}
}
@@ -117,12 +160,17 @@
// Don't animate anything that isn't independent.
if (!TransitionInfo.isIndependent(change, info)) continue;
- Animation a = loadAnimation(info.getType(), info.getFlags(), change);
+ Animation a = loadAnimation(info, change, wallpaperTransit);
if (a != null) {
- startAnimInternal(animations, a, change.getLeash(), onAnimFinish);
+ startAnimInternal(animations, a, change.getLeash(), onAnimFinish,
+ null /* position */);
+
+ if (info.getAnimationOptions() != null) {
+ attachThumbnail(animations, onAnimFinish, change, info.getAnimationOptions());
+ }
}
}
- t.apply();
+ startTransaction.apply();
// run finish now in-case there are no animations
onAnimFinish.run();
return true;
@@ -141,68 +189,111 @@
}
@Nullable
- private Animation loadAnimation(int type, int flags, TransitionInfo.Change change) {
- // TODO(b/178678389): It should handle more type animation here
+ private Animation loadAnimation(TransitionInfo info, TransitionInfo.Change change,
+ int wallpaperTransit) {
Animation a = null;
- final boolean isOpening = Transitions.isOpeningType(type);
+ final int type = info.getType();
+ final int flags = info.getFlags();
final int changeMode = change.getMode();
final int changeFlags = change.getFlags();
+ final boolean isOpeningType = Transitions.isOpeningType(type);
+ final boolean enter = Transitions.isOpeningType(changeMode);
+ final boolean isTask = change.getTaskInfo() != null;
+ final TransitionInfo.AnimationOptions options = info.getAnimationOptions();
+ final int overrideType = options != null ? options.getType() : ANIM_NONE;
+ final boolean canCustomContainer = isTask ? !sDisableCustomTaskAnimationProperty : true;
if (type == TRANSIT_RELAUNCH) {
a = mTransitionAnimation.createRelaunchAnimation(
- change.getStartAbsBounds(), mInsets, change.getEndAbsBounds());
+ change.getEndAbsBounds(), mInsets, change.getEndAbsBounds());
} else if (type == TRANSIT_KEYGUARD_GOING_AWAY) {
a = mTransitionAnimation.loadKeyguardExitAnimation(flags,
(changeFlags & FLAG_SHOW_WALLPAPER) != 0);
} else if (type == TRANSIT_KEYGUARD_UNOCCLUDE) {
a = mTransitionAnimation.loadKeyguardUnoccludeAnimation();
- } else if (changeMode == TRANSIT_OPEN && isOpening) {
- if ((changeFlags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) {
- // This received a transferred starting window, so don't animate
- return null;
- }
-
- if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) {
- a = mTransitionAnimation.loadVoiceActivityOpenAnimation(true /** enter */);
- } else if (change.getTaskInfo() != null) {
- a = mTransitionAnimation.loadDefaultAnimationAttr(
- R.styleable.WindowAnimation_taskOpenEnterAnimation);
+ } else if (overrideType == ANIM_CUSTOM
+ && (canCustomContainer || options.getOverrideTaskTransition())) {
+ a = mTransitionAnimation.loadAnimationRes(options.getPackageName(), enter
+ ? options.getEnterResId() : options.getExitResId());
+ } else if (overrideType == ANIM_OPEN_CROSS_PROFILE_APPS && enter) {
+ a = mTransitionAnimation.loadCrossProfileAppEnterAnimation();
+ } else if (overrideType == ANIM_CLIP_REVEAL) {
+ a = mTransitionAnimation.createClipRevealAnimationLocked(type, wallpaperTransit, enter,
+ change.getEndAbsBounds(), change.getEndAbsBounds(),
+ options.getTransitionBounds());
+ } else if (overrideType == ANIM_SCALE_UP) {
+ a = mTransitionAnimation.createScaleUpAnimationLocked(type, wallpaperTransit, enter,
+ change.getEndAbsBounds(), options.getTransitionBounds());
+ } else if (overrideType == ANIM_THUMBNAIL_SCALE_UP
+ || overrideType == ANIM_THUMBNAIL_SCALE_DOWN) {
+ final boolean scaleUp = overrideType == ANIM_THUMBNAIL_SCALE_UP;
+ a = mTransitionAnimation.createThumbnailEnterExitAnimationLocked(enter, scaleUp,
+ change.getEndAbsBounds(), type, wallpaperTransit, options.getThumbnail(),
+ options.getTransitionBounds());
+ } else if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_OPEN) {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+ ? R.styleable.WindowAnimation_wallpaperIntraOpenEnterAnimation
+ : R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation);
+ } else if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_CLOSE) {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+ ? R.styleable.WindowAnimation_wallpaperIntraCloseEnterAnimation
+ : R.styleable.WindowAnimation_wallpaperIntraCloseExitAnimation);
+ } else if (wallpaperTransit == WALLPAPER_TRANSITION_OPEN) {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+ ? R.styleable.WindowAnimation_wallpaperOpenEnterAnimation
+ : R.styleable.WindowAnimation_wallpaperOpenExitAnimation);
+ } else if (wallpaperTransit == WALLPAPER_TRANSITION_CLOSE) {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+ ? R.styleable.WindowAnimation_wallpaperCloseEnterAnimation
+ : R.styleable.WindowAnimation_wallpaperCloseExitAnimation);
+ } else if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) {
+ if (isOpeningType) {
+ a = mTransitionAnimation.loadVoiceActivityOpenAnimation(enter);
} else {
- a = mTransitionAnimation.loadDefaultAnimationRes(
- (changeFlags & FLAG_TRANSLUCENT) == 0
- ? R.anim.activity_open_enter : R.anim.activity_translucent_open_enter);
+ a = mTransitionAnimation.loadVoiceActivityExitAnimation(enter);
}
- } else if (changeMode == TRANSIT_TO_FRONT && isOpening) {
- if ((changeFlags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) {
- // This received a transferred starting window, so don't animate
- return null;
- }
-
- if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) {
- a = mTransitionAnimation.loadVoiceActivityOpenAnimation(true /** enter */);
+ } else if ((changeFlags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0 && isOpeningType) {
+ // This received a transferred starting window, so don't animate
+ return null;
+ } else if (type == TRANSIT_OPEN) {
+ if (isTask) {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+ ? R.styleable.WindowAnimation_taskOpenEnterAnimation
+ : R.styleable.WindowAnimation_taskOpenExitAnimation);
} else {
- a = mTransitionAnimation.loadDefaultAnimationAttr(
- R.styleable.WindowAnimation_taskToFrontEnterAnimation);
+ if ((changeFlags & FLAG_TRANSLUCENT) != 0 && enter) {
+ a = mTransitionAnimation.loadDefaultAnimationRes(
+ R.anim.activity_translucent_open_enter);
+ } else {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+ ? R.styleable.WindowAnimation_activityOpenEnterAnimation
+ : R.styleable.WindowAnimation_activityOpenExitAnimation);
+ }
}
- } else if (changeMode == TRANSIT_CLOSE && !isOpening) {
- if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) {
- a = mTransitionAnimation.loadVoiceActivityExitAnimation(false /** enter */);
- } else if (change.getTaskInfo() != null) {
- a = mTransitionAnimation.loadDefaultAnimationAttr(
- R.styleable.WindowAnimation_taskCloseExitAnimation);
+ } else if (type == TRANSIT_TO_FRONT) {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+ ? R.styleable.WindowAnimation_taskToFrontEnterAnimation
+ : R.styleable.WindowAnimation_taskToFrontExitAnimation);
+ } else if (type == TRANSIT_CLOSE) {
+ if (isTask) {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+ ? R.styleable.WindowAnimation_taskCloseEnterAnimation
+ : R.styleable.WindowAnimation_taskCloseExitAnimation);
} else {
- a = mTransitionAnimation.loadDefaultAnimationRes(
- (changeFlags & FLAG_TRANSLUCENT) == 0
- ? R.anim.activity_close_exit : R.anim.activity_translucent_close_exit);
+ if ((changeFlags & FLAG_TRANSLUCENT) != 0 && !enter) {
+ a = mTransitionAnimation.loadDefaultAnimationRes(
+ R.anim.activity_translucent_close_exit);
+ } else {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+ ? R.styleable.WindowAnimation_activityCloseEnterAnimation
+ : R.styleable.WindowAnimation_activityCloseExitAnimation);
+ }
}
- } else if (changeMode == TRANSIT_TO_BACK && !isOpening) {
- if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) {
- a = mTransitionAnimation.loadVoiceActivityExitAnimation(false /** enter */);
- } else {
- a = mTransitionAnimation.loadDefaultAnimationAttr(
- R.styleable.WindowAnimation_taskToBackExitAnimation);
- }
+ } else if (type == TRANSIT_TO_BACK) {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+ ? R.styleable.WindowAnimation_taskToBackEnterAnimation
+ : R.styleable.WindowAnimation_taskToBackExitAnimation);
} else if (changeMode == TRANSIT_CHANGE) {
// In the absence of a specific adapter, we just want to keep everything stationary.
a = new AlphaAnimation(1.f, 1.f);
@@ -210,17 +301,19 @@
}
if (a != null) {
- Rect start = change.getStartAbsBounds();
- Rect end = change.getEndAbsBounds();
+ if (!a.isInitialized()) {
+ Rect end = change.getEndAbsBounds();
+ a.initialize(end.width(), end.height(), end.width(), end.height());
+ }
a.restrictDuration(MAX_ANIMATION_DURATION);
- a.initialize(end.width(), end.height(), start.width(), start.height());
a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
}
return a;
}
private void startAnimInternal(@NonNull ArrayList<Animator> animations, @NonNull Animation anim,
- @NonNull SurfaceControl leash, @NonNull Runnable finishCallback) {
+ @NonNull SurfaceControl leash, @NonNull Runnable finishCallback,
+ @Nullable Point position) {
final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
final ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
final Transformation transformation = new Transformation();
@@ -231,11 +324,13 @@
va.addUpdateListener(animation -> {
final long currentPlayTime = Math.min(va.getDuration(), va.getCurrentPlayTime());
- applyTransformation(currentPlayTime, transaction, leash, anim, transformation, matrix);
+ applyTransformation(currentPlayTime, transaction, leash, anim, transformation, matrix,
+ position);
});
final Runnable finisher = () -> {
- applyTransformation(va.getDuration(), transaction, leash, anim, transformation, matrix);
+ applyTransformation(va.getDuration(), transaction, leash, anim, transformation, matrix,
+ position);
mTransactionPool.release(transaction);
mMainExecutor.execute(() -> {
@@ -258,9 +353,112 @@
mAnimExecutor.execute(va::start);
}
+ private void attachThumbnail(@NonNull ArrayList<Animator> animations,
+ @NonNull Runnable finishCallback, TransitionInfo.Change change,
+ TransitionInfo.AnimationOptions options) {
+ final boolean isTask = change.getTaskInfo() != null;
+ final boolean isOpen = Transitions.isOpeningType(change.getMode());
+ final boolean isClose = Transitions.isClosingType(change.getMode());
+ if (isOpen) {
+ if (options.getType() == ANIM_OPEN_CROSS_PROFILE_APPS && isTask) {
+ attachCrossProfileThunmbnailAnimation(animations, finishCallback, change);
+ } else if (options.getType() == ANIM_THUMBNAIL_SCALE_UP) {
+ attachThumbnailAnimation(animations, finishCallback, change, options);
+ }
+ } else if (isClose && options.getType() == ANIM_THUMBNAIL_SCALE_DOWN) {
+ attachThumbnailAnimation(animations, finishCallback, change, options);
+ }
+ }
+
+ private void attachCrossProfileThunmbnailAnimation(@NonNull ArrayList<Animator> animations,
+ @NonNull Runnable finishCallback, TransitionInfo.Change change) {
+ final int thumbnailDrawableRes = change.getTaskInfo().userId == mCurrentUserId
+ ? R.drawable.ic_account_circle : R.drawable.ic_corp_badge;
+ final Rect bounds = change.getEndAbsBounds();
+ final HardwareBuffer thumbnail = mTransitionAnimation.createCrossProfileAppsThumbnail(
+ thumbnailDrawableRes, bounds);
+ if (thumbnail == null) {
+ return;
+ }
+
+ final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
+ final WindowThumbnail wt = WindowThumbnail.createAndAttach(mSurfaceSession,
+ change.getLeash(), thumbnail, transaction);
+ final Animation a =
+ mTransitionAnimation.createCrossProfileAppsThumbnailAnimationLocked(bounds);
+ if (a == null) {
+ return;
+ }
+
+ final Runnable finisher = () -> {
+ wt.destroy(transaction);
+ mTransactionPool.release(transaction);
+
+ finishCallback.run();
+ };
+ a.restrictDuration(MAX_ANIMATION_DURATION);
+ a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+ startAnimInternal(animations, a, wt.getSurface(), finisher,
+ new Point(bounds.left, bounds.top));
+ }
+
+ private void attachThumbnailAnimation(@NonNull ArrayList<Animator> animations,
+ @NonNull Runnable finishCallback, TransitionInfo.Change change,
+ TransitionInfo.AnimationOptions options) {
+ final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
+ final WindowThumbnail wt = WindowThumbnail.createAndAttach(mSurfaceSession,
+ change.getLeash(), options.getThumbnail(), transaction);
+ final Rect bounds = change.getEndAbsBounds();
+ final int orientation = mContext.getResources().getConfiguration().orientation;
+ final Animation a = mTransitionAnimation.createThumbnailAspectScaleAnimationLocked(bounds,
+ mInsets, options.getThumbnail(), orientation, null /* startRect */,
+ options.getTransitionBounds(), options.getType() == ANIM_THUMBNAIL_SCALE_UP);
+
+ final Runnable finisher = () -> {
+ wt.destroy(transaction);
+ mTransactionPool.release(transaction);
+
+ finishCallback.run();
+ };
+ a.restrictDuration(MAX_ANIMATION_DURATION);
+ a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+ startAnimInternal(animations, a, wt.getSurface(), finisher, null /* position */);
+ }
+
+ private static int getWallpaperTransitType(TransitionInfo info) {
+ boolean hasOpenWallpaper = false;
+ boolean hasCloseWallpaper = false;
+
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if ((change.getFlags() & FLAG_SHOW_WALLPAPER) != 0) {
+ if (Transitions.isOpeningType(change.getMode())) {
+ hasOpenWallpaper = true;
+ } else if (Transitions.isClosingType(change.getMode())) {
+ hasCloseWallpaper = true;
+ }
+ }
+ }
+
+ if (hasOpenWallpaper && hasCloseWallpaper) {
+ return Transitions.isOpeningType(info.getType())
+ ? WALLPAPER_TRANSITION_INTRA_OPEN : WALLPAPER_TRANSITION_INTRA_CLOSE;
+ } else if (hasOpenWallpaper) {
+ return WALLPAPER_TRANSITION_OPEN;
+ } else if (hasCloseWallpaper) {
+ return WALLPAPER_TRANSITION_CLOSE;
+ } else {
+ return WALLPAPER_TRANSITION_NONE;
+ }
+ }
+
private static void applyTransformation(long time, SurfaceControl.Transaction t,
- SurfaceControl leash, Animation anim, Transformation transformation, float[] matrix) {
+ SurfaceControl leash, Animation anim, Transformation transformation, float[] matrix,
+ Point position) {
anim.getTransformation(time, transformation);
+ if (position != null) {
+ transformation.getMatrix().postTranslate(position.x, position.y);
+ }
t.setMatrix(leash, transformation.getMatrix(), matrix);
t.setAlpha(leash, transformation.getAlpha());
t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
index 4da6664..6bd8053 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
@@ -57,7 +57,8 @@
@Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
if (mTransition != transition) return false;
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Using registered One-shot remote"
@@ -70,19 +71,24 @@
};
IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
@Override
- public void onTransitionFinished(WindowContainerTransaction wct) {
+ public void onTransitionFinished(WindowContainerTransaction wct,
+ SurfaceControl.Transaction sct) {
if (mRemote.asBinder() != null) {
mRemote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */);
}
- mMainExecutor.execute(
- () -> finishCallback.onTransitionFinished(wct, null /* wctCB */));
+ mMainExecutor.execute(() -> {
+ if (sct != null) {
+ finishTransaction.merge(sct);
+ }
+ finishCallback.onTransitionFinished(wct, null /* wctCB */);
+ });
}
};
try {
if (mRemote.asBinder() != null) {
mRemote.asBinder().linkToDeath(remoteDied, 0 /* flags */);
}
- mRemote.startAnimation(transition, info, t, cb);
+ mRemote.startAnimation(transition, info, startTransaction, cb);
} catch (RemoteException e) {
Log.e(Transitions.TAG, "Error running remote transition.", e);
if (mRemote.asBinder() != null) {
@@ -102,7 +108,8 @@
IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
@Override
- public void onTransitionFinished(WindowContainerTransaction wct) {
+ public void onTransitionFinished(WindowContainerTransaction wct,
+ SurfaceControl.Transaction sct) {
mMainExecutor.execute(
() -> finishCallback.onTransitionFinished(wct, null /* wctCB */));
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index 9bfb261..e67186f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -99,7 +99,8 @@
@Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
IRemoteTransition pendingRemote = mRequestedRemotes.get(transition);
if (pendingRemote == null) {
@@ -132,11 +133,15 @@
};
IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
@Override
- public void onTransitionFinished(WindowContainerTransaction wct) {
+ public void onTransitionFinished(WindowContainerTransaction wct,
+ SurfaceControl.Transaction sct) {
if (remote.asBinder() != null) {
remote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */);
}
mMainExecutor.execute(() -> {
+ if (sct != null) {
+ finishTransaction.merge(sct);
+ }
mRequestedRemotes.remove(transition);
finishCallback.onTransitionFinished(wct, null /* wctCB */);
});
@@ -146,7 +151,7 @@
if (remote.asBinder() != null) {
remote.asBinder().linkToDeath(remoteDied, 0 /* flags */);
}
- remote.startAnimation(transition, info, t, cb);
+ remote.startAnimation(transition, info, startTransaction, cb);
} catch (RemoteException e) {
Log.e(Transitions.TAG, "Error running remote transition.", e);
if (remote.asBinder() != null) {
@@ -170,7 +175,8 @@
IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
@Override
- public void onTransitionFinished(WindowContainerTransaction wct) {
+ public void onTransitionFinished(WindowContainerTransaction wct,
+ SurfaceControl.Transaction sct) {
mMainExecutor.execute(() -> {
if (!mRequestedRemotes.containsKey(mergeTarget)) {
Log.e(TAG, "Merged transition finished after it's mergeTarget (the "
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 60707cc..64faed2 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
@@ -77,6 +77,9 @@
/** Transition type for launching 2 tasks simultaneously. */
public static final int TRANSIT_SPLIT_SCREEN_PAIR_OPEN = TRANSIT_FIRST_CUSTOM + 2;
+ /** Transition type for exiting PIP via the Shell, either via Expand or Dismiss. */
+ public static final int TRANSIT_EXIT_PIP = TRANSIT_FIRST_CUSTOM + 3;
+
private final WindowOrganizer mOrganizer;
private final Context mContext;
private final ShellExecutor mMainExecutor;
@@ -382,7 +385,7 @@
}
boolean startAnimation(@NonNull ActiveTransition active, TransitionHandler handler) {
- return handler.startAnimation(active.mToken, active.mInfo, active.mStartT,
+ return handler.startAnimation(active.mToken, active.mInfo, active.mStartT, active.mFinishT,
(wct, cb) -> onFinish(active.mToken, wct, cb));
}
@@ -566,12 +569,19 @@
* Starts a transition animation. This is always called if handleRequest returned non-null
* for a particular transition. Otherwise, it is only called if no other handler before
* it handled the transition.
- *
+ * @param startTransaction the transaction given to the handler to be applied before the
+ * transition animation. Note the handler is expected to call on
+ * {@link SurfaceControl.Transaction#apply()} for startTransaction.
+ * @param finishTransaction the transaction given to the handler to be applied after the
+ * transition animation. Unlike startTransaction, the handler is NOT
+ * expected to apply this transaction. The Transition system will
+ * apply it when finishCallback is called.
* @param finishCallback Call this when finished. This MUST be called on main thread.
* @return true if transition was handled, false if not (falls-back to default).
*/
boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
@NonNull TransitionFinishCallback finishCallback);
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/WindowThumbnail.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/WindowThumbnail.java
new file mode 100644
index 0000000..2c668ed
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/WindowThumbnail.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.transition;
+
+import android.graphics.ColorSpace;
+import android.graphics.GraphicBuffer;
+import android.graphics.PixelFormat;
+import android.hardware.HardwareBuffer;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+
+/**
+ * Represents a surface that is displayed over a transition surface.
+ */
+class WindowThumbnail {
+
+ private SurfaceControl mSurfaceControl;
+
+ private WindowThumbnail() {}
+
+ /** Create a thumbnail surface and attach it over a parent surface. */
+ static WindowThumbnail createAndAttach(SurfaceSession surfaceSession, SurfaceControl parent,
+ HardwareBuffer thumbnailHeader, SurfaceControl.Transaction t) {
+ WindowThumbnail windowThumbnail = new WindowThumbnail();
+ windowThumbnail.mSurfaceControl = new SurfaceControl.Builder(surfaceSession)
+ .setParent(parent)
+ .setName("WindowThumanil : " + parent.toString())
+ .setCallsite("WindowThumanil")
+ .setFormat(PixelFormat.TRANSLUCENT)
+ .build();
+
+ GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(thumbnailHeader);
+ t.setBuffer(windowThumbnail.mSurfaceControl, graphicBuffer);
+ t.setColorSpace(windowThumbnail.mSurfaceControl, ColorSpace.get(ColorSpace.Named.SRGB));
+ t.setLayer(windowThumbnail.mSurfaceControl, Integer.MAX_VALUE);
+ t.show(windowThumbnail.mSurfaceControl);
+ t.apply();
+
+ return windowThumbnail;
+ }
+
+ SurfaceControl getSurface() {
+ return mSurfaceControl;
+ }
+
+ /** Remove the thumbnail surface and release the surface. */
+ void destroy(SurfaceControl.Transaction t) {
+ if (mSurfaceControl == null) {
+ return;
+ }
+
+ t.remove(mSurfaceControl);
+ t.apply();
+ mSurfaceControl.release();
+ mSurfaceControl = null;
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
index f4dd7de..d4b4e5d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
@@ -62,7 +62,7 @@
stringExtras: Map<String, String>
) {
super.launchViaIntent(wmHelper, expectedWindowName, action, stringExtras)
- wmHelper.waitFor { it.wmState.hasPipWindow() }
+ wmHelper.waitFor("hasPipWindow") { it.wmState.hasPipWindow() }
}
private fun focusOnObject(selector: BySelector): Boolean {
@@ -84,7 +84,7 @@
clickObject(ENTER_PIP_BUTTON_ID)
// Wait on WMHelper or simply wait for 3 seconds
- wmHelper?.waitFor { it.wmState.hasPipWindow() } ?: SystemClock.sleep(3_000)
+ wmHelper?.waitFor("hasPipWindow") { it.wmState.hasPipWindow() } ?: SystemClock.sleep(3_000)
}
fun clickStartMediaSessionButton() {
@@ -137,7 +137,7 @@
}
// Wait for animation to complete.
- wmHelper.waitFor { !it.wmState.hasPipWindow() }
+ wmHelper.waitFor("!hasPipWindow") { !it.wmState.hasPipWindow() }
wmHelper.waitForHomeActivityVisible()
}
@@ -167,7 +167,7 @@
val windowRect = windowRegion.bounds
uiDevice.click(windowRect.centerX(), windowRect.centerY())
uiDevice.click(windowRect.centerX(), windowRect.centerY())
- wmHelper.waitFor { !it.wmState.hasPipWindow() }
+ wmHelper.waitFor("!hasPipWindow") { !it.wmState.hasPipWindow() }
wmHelper.waitForAppTransitionIdle()
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index b6af260..80fedd4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -44,7 +44,7 @@
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = buildTransition(eachRun = true, stringExtras = emptyMap()) {
transitions {
- pipApp.clickEnterPipButton()
+ pipApp.clickEnterPipButton(wmHelper)
pipApp.expandPipWindow(wmHelper)
}
}
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 20ac5bf..1cbad15 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
@@ -47,6 +47,8 @@
import androidx.test.filters.SmallTest;
import com.android.wm.shell.common.HandlerExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.SyncTransactionQueue.TransactionRunnable;
import org.junit.After;
import org.junit.Before;
@@ -71,6 +73,8 @@
ShellTaskOrganizer mOrganizer;
@Mock
HandlerExecutor mExecutor;
+ @Mock
+ SyncTransactionQueue mSyncQueue;
SurfaceSession mSession;
SurfaceControl mLeash;
@@ -99,7 +103,14 @@
}).when(mExecutor).execute(any());
when(mOrganizer.getExecutor()).thenReturn(mExecutor);
- mTaskView = new TaskView(mContext, mOrganizer);
+
+ doAnswer((InvocationOnMock invocationOnMock) -> {
+ final TransactionRunnable r = invocationOnMock.getArgument(0);
+ r.runWithTransaction(new SurfaceControl.Transaction());
+ return null;
+ }).when(mSyncQueue).runInSync(any());
+
+ mTaskView = new TaskView(mContext, mOrganizer, mSyncQueue);
mTaskView.setListener(mExecutor, mViewListener);
}
@@ -112,7 +123,7 @@
@Test
public void testSetPendingListener_throwsException() {
- TaskView taskView = new TaskView(mContext, mOrganizer);
+ TaskView taskView = new TaskView(mContext, mOrganizer, mSyncQueue);
taskView.setListener(mExecutor, mViewListener);
try {
taskView.setListener(mExecutor, mViewListener);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index 952dc31..e138595 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -61,7 +61,7 @@
mSplitLayout = new SplitLayout(
"TestSplitLayout",
mContext,
- getConfiguration(false),
+ getConfiguration(),
mSplitLayoutHandler,
b -> b.setParent(mRootLeash),
mDisplayImeController,
@@ -71,9 +71,22 @@
@Test
@UiThreadTest
public void testUpdateConfiguration() {
- mSplitLayout.init();
- assertThat(mSplitLayout.updateConfiguration(getConfiguration(false))).isFalse();
- assertThat(mSplitLayout.updateConfiguration(getConfiguration(true))).isTrue();
+ final Configuration config = getConfiguration();
+
+ // Verify it returns true if new config won't affect split layout.
+ assertThat(mSplitLayout.updateConfiguration(config)).isFalse();
+
+ // Verify updateConfiguration returns true if the orientation changed.
+ config.orientation = ORIENTATION_LANDSCAPE;
+ assertThat(mSplitLayout.updateConfiguration(config)).isTrue();
+
+ // Verify updateConfiguration returns true if it rotated.
+ config.windowConfiguration.setRotation(1);
+ assertThat(mSplitLayout.updateConfiguration(config)).isTrue();
+
+ // Verify updateConfiguration returns true if the root bounds changed.
+ config.windowConfiguration.setBounds(new Rect(0, 0, 2160, 1080));
+ assertThat(mSplitLayout.updateConfiguration(config)).isTrue();
}
@Test
@@ -108,12 +121,13 @@
verify(mSplitLayoutHandler).onSnappedToDismiss(eq(true));
}
- private static Configuration getConfiguration(boolean isLandscape) {
+ private static Configuration getConfiguration() {
final Configuration configuration = new Configuration();
configuration.unset();
- configuration.orientation = isLandscape ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
+ configuration.orientation = ORIENTATION_PORTRAIT;
+ configuration.windowConfiguration.setRotation(0);
configuration.windowConfiguration.setBounds(
- new Rect(0, 0, isLandscape ? 2160 : 1080, isLandscape ? 1080 : 2160));
+ new Rect(0, 0, 1080, 2160));
return configuration;
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 9d7c82b..0270093 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -79,6 +79,7 @@
@Mock private ShellTaskOrganizer mMockShellTaskOrganizer;
private TestShellExecutor mMainExecutor;
private PipBoundsState mPipBoundsState;
+ private PipTransitionState mPipTransitionState;
private PipBoundsAlgorithm mPipBoundsAlgorithm;
private ComponentName mComponent1;
@@ -90,11 +91,12 @@
mComponent1 = new ComponentName(mContext, "component1");
mComponent2 = new ComponentName(mContext, "component2");
mPipBoundsState = new PipBoundsState(mContext);
+ mPipTransitionState = new PipTransitionState();
mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState,
new PipSnapAlgorithm());
mMainExecutor = new TestShellExecutor();
mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mContext,
- mMockSyncTransactionQueue, mPipBoundsState,
+ mMockSyncTransactionQueue, mPipTransitionState, mPipBoundsState,
mPipBoundsAlgorithm, mMockPhonePipMenuController,
mMockPipAnimationController, mMockPipSurfaceTransactionHelper,
mMockPipTransitionController, mMockOptionalSplitScreen, mMockDisplayController,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index aca80f3..5d9d50f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -131,6 +131,7 @@
mSideStage.onTaskAppeared(mSideChild, createMockSurface());
boolean accepted = mStageCoordinator.startAnimation(transition, info,
mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
mock(Transitions.TransitionFinishCallback.class));
assertTrue(accepted);
@@ -168,6 +169,7 @@
mSideStage.onTaskAppeared(newTask, createMockSurface());
boolean accepted = mStageCoordinator.startAnimation(transition, info,
mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
mock(Transitions.TransitionFinishCallback.class));
assertFalse(accepted);
assertTrue(mStageCoordinator.isSplitScreenVisible());
@@ -188,6 +190,7 @@
mSideStage.onTaskVanished(newTask);
accepted = mStageCoordinator.startAnimation(transition, info,
mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
mock(Transitions.TransitionFinishCallback.class));
assertFalse(accepted);
assertTrue(mStageCoordinator.isSplitScreenVisible());
@@ -223,6 +226,7 @@
mSideStage.onTaskVanished(mSideChild);
mStageCoordinator.startAnimation(transition, info,
mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
mock(Transitions.TransitionFinishCallback.class));
assertFalse(mStageCoordinator.isSplitScreenVisible());
}
@@ -244,6 +248,7 @@
mSideStage.onTaskVanished(mSideChild);
boolean accepted = mStageCoordinator.startAnimation(transition, info,
mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
mock(Transitions.TransitionFinishCallback.class));
assertTrue(accepted);
assertFalse(mStageCoordinator.isSplitScreenVisible());
@@ -274,6 +279,7 @@
mSideStage.onTaskVanished(mSideChild);
boolean accepted = mStageCoordinator.startAnimation(transition, info,
mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
mock(Transitions.TransitionFinishCallback.class));
assertTrue(accepted);
assertFalse(mStageCoordinator.isSplitScreenVisible());
@@ -298,6 +304,7 @@
mSideStage.onTaskAppeared(mSideChild, createMockSurface());
mStageCoordinator.startAnimation(enterTransit, enterInfo,
mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
mock(Transitions.TransitionFinishCallback.class));
mMainStage.activate(new Rect(0, 0, 100, 100), new WindowContainerTransaction());
}
@@ -335,10 +342,11 @@
@Override
public void startAnimation(IBinder transition, TransitionInfo info,
- SurfaceControl.Transaction t, IRemoteTransitionFinishedCallback finishCallback)
+ SurfaceControl.Transaction startTransaction,
+ IRemoteTransitionFinishedCallback finishCallback)
throws RemoteException {
mCalled = true;
- finishCallback.onTransitionFinished(mRemoteFinishWCT);
+ finishCallback.onTransitionFinished(mRemoteFinishWCT, null /* sct */);
}
@Override
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 2d2ab2c..a2b1f64 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
@@ -127,11 +127,13 @@
TestTransitionHandler testHandler = new TestTransitionHandler() {
@Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t,
+ @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, t, finishCallback);
+ return super.startAnimation(transition, info, startTransaction,
+ finishTransaction, finishCallback);
}
}
return false;
@@ -211,7 +213,7 @@
SurfaceControl.Transaction t,
IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
remoteCalled[0] = true;
- finishCallback.onTransitionFinished(remoteFinishWCT);
+ finishCallback.onTransitionFinished(remoteFinishWCT, null /* sct */);
}
@Override
@@ -285,7 +287,7 @@
SurfaceControl.Transaction t,
IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
remoteCalled[0] = true;
- finishCallback.onTransitionFinished(null /* wct */);
+ finishCallback.onTransitionFinished(null /* wct */, null /* sct */);
}
@Override
@@ -332,7 +334,7 @@
SurfaceControl.Transaction t,
IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
remoteCalled[0] = true;
- finishCallback.onTransitionFinished(remoteFinishWCT);
+ finishCallback.onTransitionFinished(remoteFinishWCT, null /* sct */);
}
@Override
@@ -358,9 +360,11 @@
oneShot.setTransition(transitToken);
IBinder anotherToken = new Binder();
assertFalse(oneShot.startAnimation(anotherToken, new TransitionInfo(transitType, 0),
- mock(SurfaceControl.Transaction.class), testFinish));
+ mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class),
+ testFinish));
assertTrue(oneShot.startAnimation(transitToken, new TransitionInfo(transitType, 0),
- mock(SurfaceControl.Transaction.class), testFinish));
+ mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class),
+ testFinish));
}
@Test
@@ -477,7 +481,8 @@
@Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
mFinishes.add(finishCallback);
return true;
diff --git a/media/Android.bp b/media/Android.bp
index a66236e..cf4a0b1 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -8,23 +8,6 @@
}
aidl_interface {
- name: "audio_common-aidl",
- unstable: true,
- host_supported: true,
- vendor_available: true,
- local_include_dir: "aidl",
- double_loadable: true,
- srcs: [
- "aidl/android/media/audio/common/AudioChannelMask.aidl",
- "aidl/android/media/audio/common/AudioConfig.aidl",
- "aidl/android/media/audio/common/AudioFormat.aidl",
- "aidl/android/media/audio/common/AudioOffloadInfo.aidl",
- "aidl/android/media/audio/common/AudioStreamType.aidl",
- "aidl/android/media/audio/common/AudioUsage.aidl",
- ],
-}
-
-aidl_interface {
name: "media_permission-aidl",
unstable: true,
host_supported: true,
@@ -40,30 +23,92 @@
name: "soundtrigger_middleware-aidl",
unstable: true,
local_include_dir: "aidl",
+ backend: {
+ java: {
+ sdk_version: "module_current",
+ },
+ },
srcs: [
- "aidl/android/media/soundtrigger_middleware/AudioCapabilities.aidl",
- "aidl/android/media/soundtrigger_middleware/ConfidenceLevel.aidl",
"aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl",
"aidl/android/media/soundtrigger_middleware/ISoundTriggerMiddlewareService.aidl",
"aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl",
- "aidl/android/media/soundtrigger_middleware/ModelParameter.aidl",
- "aidl/android/media/soundtrigger_middleware/ModelParameterRange.aidl",
- "aidl/android/media/soundtrigger_middleware/Phrase.aidl",
- "aidl/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl",
- "aidl/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl",
- "aidl/android/media/soundtrigger_middleware/PhraseSoundModel.aidl",
- "aidl/android/media/soundtrigger_middleware/RecognitionConfig.aidl",
- "aidl/android/media/soundtrigger_middleware/RecognitionEvent.aidl",
- "aidl/android/media/soundtrigger_middleware/RecognitionMode.aidl",
- "aidl/android/media/soundtrigger_middleware/RecognitionStatus.aidl",
- "aidl/android/media/soundtrigger_middleware/SoundModel.aidl",
- "aidl/android/media/soundtrigger_middleware/SoundModelType.aidl",
"aidl/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl",
- "aidl/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl",
- "aidl/android/media/soundtrigger_middleware/Status.aidl",
],
imports: [
- "audio_common-aidl",
+ "android.media.soundtrigger.types",
"media_permission-aidl",
],
}
+
+aidl_interface {
+ name: "android.media.audio.common.types",
+ vendor_available: true,
+ host_supported: true,
+ double_loadable: true,
+ flags: ["-Werror", "-Weverything", ],
+ local_include_dir: "aidl",
+ srcs: [
+ "aidl/android/media/audio/common/AudioChannelMask.aidl",
+ "aidl/android/media/audio/common/AudioConfig.aidl",
+ "aidl/android/media/audio/common/AudioFormat.aidl",
+ "aidl/android/media/audio/common/AudioOffloadInfo.aidl",
+ "aidl/android/media/audio/common/AudioStreamType.aidl",
+ "aidl/android/media/audio/common/AudioUsage.aidl",
+ ],
+ stability: "vintf",
+ backend: {
+ cpp: {
+ enabled: true,
+ },
+ java: {
+ sdk_version: "module_current",
+ },
+ ndk: {
+ vndk: {
+ enabled: true,
+ },
+ },
+ },
+}
+
+aidl_interface {
+ name: "android.media.soundtrigger.types",
+ vendor_available: true,
+ flags: ["-Werror", "-Weverything", ],
+ local_include_dir: "aidl",
+ srcs: [
+ "aidl/android/media/soundtrigger/AudioCapabilities.aidl",
+ "aidl/android/media/soundtrigger/ConfidenceLevel.aidl",
+ "aidl/android/media/soundtrigger/ModelParameter.aidl",
+ "aidl/android/media/soundtrigger/ModelParameterRange.aidl",
+ "aidl/android/media/soundtrigger/Phrase.aidl",
+ "aidl/android/media/soundtrigger/PhraseRecognitionEvent.aidl",
+ "aidl/android/media/soundtrigger/PhraseRecognitionExtra.aidl",
+ "aidl/android/media/soundtrigger/PhraseSoundModel.aidl",
+ "aidl/android/media/soundtrigger/Properties.aidl",
+ "aidl/android/media/soundtrigger/RecognitionConfig.aidl",
+ "aidl/android/media/soundtrigger/RecognitionEvent.aidl",
+ "aidl/android/media/soundtrigger/RecognitionMode.aidl",
+ "aidl/android/media/soundtrigger/RecognitionStatus.aidl",
+ "aidl/android/media/soundtrigger/SoundModel.aidl",
+ "aidl/android/media/soundtrigger/SoundModelType.aidl",
+ "aidl/android/media/soundtrigger/Status.aidl",
+ ],
+ stability: "vintf",
+ backend: {
+ cpp: {
+ enabled: true,
+ },
+ java: {
+ sdk_version: "module_current",
+ },
+ ndk: {
+ vndk: {
+ enabled: true,
+ },
+ },
+ },
+ imports: [
+ "android.media.audio.common.types",
+ ],
+}
diff --git a/media/aidl/android/media/audio/common/AudioChannelMask.aidl b/media/aidl/android/media/audio/common/AudioChannelMask.aidl
index b9b08e6..17be8dd 100644
--- a/media/aidl/android/media/audio/common/AudioChannelMask.aidl
+++ b/media/aidl/android/media/audio/common/AudioChannelMask.aidl
@@ -57,8 +57,11 @@
* checking the channel mask, the implementer should look for ways to fix it
* with additional information outside of the mask.
*
+ * TODO: this should be replaced with strings or other mechanisms that are easy to extend, in line
+ * with the audio HAL conventions.
* {@hide}
*/
+@VintfStability
@Backing(type="int")
enum AudioChannelMask {
/**
@@ -113,42 +116,43 @@
*/
OUT_HAPTIC_A = 0x20000000,
OUT_HAPTIC_B = 0x10000000,
-// TODO(ytai): Aliases not currently supported in AIDL - can inline the values.
-// OUT_MONO = OUT_FRONT_LEFT,
-// OUT_STEREO = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT),
-// OUT_2POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_LOW_FREQUENCY),
-// OUT_2POINT0POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
-// OUT_2POINT1POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT | OUT_LOW_FREQUENCY),
-// OUT_3POINT0POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
-// OUT_3POINT1POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT | OUT_LOW_FREQUENCY),
-// OUT_QUAD = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_BACK_LEFT | OUT_BACK_RIGHT),
-// OUT_QUAD_BACK = OUT_QUAD,
-// /**
-// * like OUT_QUAD_BACK with *_SIDE_* instead of *_BACK_*
-// */
-// OUT_QUAD_SIDE = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_SIDE_LEFT | OUT_SIDE_RIGHT),
-// OUT_SURROUND = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_BACK_CENTER),
-// OUT_PENTA = (OUT_QUAD | OUT_FRONT_CENTER),
-// OUT_5POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_BACK_LEFT | OUT_BACK_RIGHT),
-// OUT_5POINT1_BACK = OUT_5POINT1,
-// /**
-// * like OUT_5POINT1_BACK with *_SIDE_* instead of *_BACK_*
-// */
-// OUT_5POINT1_SIDE = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_SIDE_LEFT | OUT_SIDE_RIGHT),
-// OUT_5POINT1POINT2 = (OUT_5POINT1 | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
-// OUT_5POINT1POINT4 = (OUT_5POINT1 | OUT_TOP_FRONT_LEFT | OUT_TOP_FRONT_RIGHT | OUT_TOP_BACK_LEFT | OUT_TOP_BACK_RIGHT),
-// OUT_6POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_BACK_LEFT | OUT_BACK_RIGHT | OUT_BACK_CENTER),
-// /**
-// * matches the correct AudioFormat.CHANNEL_OUT_7POINT1_SURROUND
-// */
-// OUT_7POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_BACK_LEFT | OUT_BACK_RIGHT | OUT_SIDE_LEFT | OUT_SIDE_RIGHT),
-// OUT_7POINT1POINT2 = (OUT_7POINT1 | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
-// OUT_7POINT1POINT4 = (OUT_7POINT1 | OUT_TOP_FRONT_LEFT | OUT_TOP_FRONT_RIGHT | OUT_TOP_BACK_LEFT | OUT_TOP_BACK_RIGHT),
-// OUT_MONO_HAPTIC_A = (OUT_FRONT_LEFT | OUT_HAPTIC_A),
-// OUT_STEREO_HAPTIC_A = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_HAPTIC_A),
-// OUT_HAPTIC_AB = (OUT_HAPTIC_A | OUT_HAPTIC_B),
-// OUT_MONO_HAPTIC_AB = (OUT_FRONT_LEFT | OUT_HAPTIC_A | OUT_HAPTIC_B),
-// OUT_STEREO_HAPTIC_AB = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_HAPTIC_A | OUT_HAPTIC_B),
+
+ OUT_MONO = OUT_FRONT_LEFT,
+ OUT_STEREO = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT),
+ OUT_2POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_LOW_FREQUENCY),
+ OUT_2POINT0POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
+ OUT_2POINT1POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT | OUT_LOW_FREQUENCY),
+ OUT_3POINT0POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
+ OUT_3POINT1POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT | OUT_LOW_FREQUENCY),
+ OUT_QUAD = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_BACK_LEFT | OUT_BACK_RIGHT),
+ OUT_QUAD_BACK = OUT_QUAD,
+ /**
+ * like OUT_QUAD_BACK with *_SIDE_* instead of *_BACK_*
+ */
+ OUT_QUAD_SIDE = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_SIDE_LEFT | OUT_SIDE_RIGHT),
+ OUT_SURROUND = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_BACK_CENTER),
+ OUT_PENTA = (OUT_QUAD | OUT_FRONT_CENTER),
+ OUT_5POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_BACK_LEFT | OUT_BACK_RIGHT),
+ OUT_5POINT1_BACK = OUT_5POINT1,
+ /**
+ * like OUT_5POINT1_BACK with *_SIDE_* instead of *_BACK_*
+ */
+ OUT_5POINT1_SIDE = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_SIDE_LEFT | OUT_SIDE_RIGHT),
+ OUT_5POINT1POINT2 = (OUT_5POINT1 | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
+ OUT_5POINT1POINT4 = (OUT_5POINT1 | OUT_TOP_FRONT_LEFT | OUT_TOP_FRONT_RIGHT | OUT_TOP_BACK_LEFT | OUT_TOP_BACK_RIGHT),
+ OUT_6POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_BACK_LEFT | OUT_BACK_RIGHT | OUT_BACK_CENTER),
+ /**
+ * matches the correct AudioFormat.CHANNEL_OUT_7POINT1_SURROUND
+ */
+ OUT_7POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_BACK_LEFT | OUT_BACK_RIGHT | OUT_SIDE_LEFT | OUT_SIDE_RIGHT),
+ OUT_7POINT1POINT2 = (OUT_7POINT1 | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
+ OUT_7POINT1POINT4 = (OUT_7POINT1 | OUT_TOP_FRONT_LEFT | OUT_TOP_FRONT_RIGHT | OUT_TOP_BACK_LEFT | OUT_TOP_BACK_RIGHT),
+ OUT_MONO_HAPTIC_A = (OUT_FRONT_LEFT | OUT_HAPTIC_A),
+ OUT_STEREO_HAPTIC_A = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_HAPTIC_A),
+ OUT_HAPTIC_AB = (OUT_HAPTIC_A | OUT_HAPTIC_B),
+ OUT_MONO_HAPTIC_AB = (OUT_FRONT_LEFT | OUT_HAPTIC_A | OUT_HAPTIC_B),
+ OUT_STEREO_HAPTIC_AB = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_HAPTIC_A | OUT_HAPTIC_B),
+
/**
* These are bits only, not complete values
*
diff --git a/media/aidl/android/media/audio/common/AudioConfig.aidl b/media/aidl/android/media/audio/common/AudioConfig.aidl
index 50dd796..d128561 100644
--- a/media/aidl/android/media/audio/common/AudioConfig.aidl
+++ b/media/aidl/android/media/audio/common/AudioConfig.aidl
@@ -27,10 +27,12 @@
*
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
+@VintfStability
parcelable AudioConfig {
int sampleRateHz;
int channelMask;
- AudioFormat format;
+ AudioFormat format = AudioFormat.INVALID;
AudioOffloadInfo offloadInfo;
long frameCount;
}
diff --git a/media/aidl/android/media/audio/common/AudioFormat.aidl b/media/aidl/android/media/audio/common/AudioFormat.aidl
index aadc8e2..73fbca2 100644
--- a/media/aidl/android/media/audio/common/AudioFormat.aidl
+++ b/media/aidl/android/media/audio/common/AudioFormat.aidl
@@ -32,6 +32,7 @@
*
* {@hide}
*/
+@VintfStability
@Backing(type="int")
enum AudioFormat {
INVALID = 0xFFFFFFFF,
diff --git a/media/aidl/android/media/audio/common/AudioOffloadInfo.aidl b/media/aidl/android/media/audio/common/AudioOffloadInfo.aidl
index ec10d711..7be5e6a 100644
--- a/media/aidl/android/media/audio/common/AudioOffloadInfo.aidl
+++ b/media/aidl/android/media/audio/common/AudioOffloadInfo.aidl
@@ -28,17 +28,19 @@
*
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
+@VintfStability
parcelable AudioOffloadInfo {
int sampleRateHz;
int channelMask;
- AudioFormat format;
- AudioStreamType streamType;
+ AudioFormat format = AudioFormat.INVALID;
+ AudioStreamType streamType = AudioStreamType.INVALID;
int bitRatePerSecond;
long durationMicroseconds;
boolean hasVideo;
boolean isStreaming;
int bitWidth;
int bufferSize;
- AudioUsage usage;
+ AudioUsage usage = AudioUsage.INVALID;
}
diff --git a/media/aidl/android/media/audio/common/AudioStreamType.aidl b/media/aidl/android/media/audio/common/AudioStreamType.aidl
index c545667..8b70367 100644
--- a/media/aidl/android/media/audio/common/AudioStreamType.aidl
+++ b/media/aidl/android/media/audio/common/AudioStreamType.aidl
@@ -26,8 +26,14 @@
*
* {@hide}
*/
+@VintfStability
@Backing(type="int")
enum AudioStreamType {
+ /**
+ * Used as default value in parcelables to indicate that a value was not set.
+ * Should never be considered a valid setting, except for backward compatibility scenarios.
+ */
+ INVALID = -2,
DEFAULT = -1,
MIN = 0,
VOICE_CALL = 0,
diff --git a/media/aidl/android/media/audio/common/AudioUsage.aidl b/media/aidl/android/media/audio/common/AudioUsage.aidl
index ef34816..028eefe 100644
--- a/media/aidl/android/media/audio/common/AudioUsage.aidl
+++ b/media/aidl/android/media/audio/common/AudioUsage.aidl
@@ -22,8 +22,14 @@
/**
* {@hide}
*/
+@VintfStability
@Backing(type="int")
enum AudioUsage {
+ /**
+ * Used as default value in parcelables to indicate that a value was not set.
+ * Should never be considered a valid setting, except for backward compatibility scenarios.
+ */
+ INVALID = -1,
UNKNOWN = 0,
MEDIA = 1,
VOICE_COMMUNICATION = 2,
diff --git a/media/aidl/android/media/permission/Identity.aidl b/media/aidl/android/media/permission/Identity.aidl
index 3638904..5249786 100644
--- a/media/aidl/android/media/permission/Identity.aidl
+++ b/media/aidl/android/media/permission/Identity.aidl
@@ -20,6 +20,7 @@
*
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
parcelable Identity {
/** Linux user ID. */
int uid = -1;
diff --git a/media/aidl/android/media/soundtrigger_middleware/AudioCapabilities.aidl b/media/aidl/android/media/soundtrigger/AudioCapabilities.aidl
similarity index 94%
rename from media/aidl/android/media/soundtrigger_middleware/AudioCapabilities.aidl
rename to media/aidl/android/media/soundtrigger/AudioCapabilities.aidl
index 97a8849..7b0825b 100644
--- a/media/aidl/android/media/soundtrigger_middleware/AudioCapabilities.aidl
+++ b/media/aidl/android/media/soundtrigger/AudioCapabilities.aidl
@@ -13,12 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
/**
* AudioCapabilities supported by the implemented HAL driver.
* @hide
*/
+@VintfStability
@Backing(type="int")
enum AudioCapabilities {
/**
diff --git a/media/aidl/android/media/soundtrigger_middleware/ConfidenceLevel.aidl b/media/aidl/android/media/soundtrigger/ConfidenceLevel.aidl
similarity index 91%
rename from media/aidl/android/media/soundtrigger_middleware/ConfidenceLevel.aidl
rename to media/aidl/android/media/soundtrigger/ConfidenceLevel.aidl
index 3dbc705..3fcba40 100644
--- a/media/aidl/android/media/soundtrigger_middleware/ConfidenceLevel.aidl
+++ b/media/aidl/android/media/soundtrigger/ConfidenceLevel.aidl
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
/**
* A recognition confidence level.
@@ -21,6 +21,8 @@
*
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
+@VintfStability
parcelable ConfidenceLevel {
/** user ID. */
int userId;
diff --git a/media/aidl/android/media/soundtrigger_middleware/ModelParameter.aidl b/media/aidl/android/media/soundtrigger/ModelParameter.aidl
similarity index 95%
rename from media/aidl/android/media/soundtrigger_middleware/ModelParameter.aidl
rename to media/aidl/android/media/soundtrigger/ModelParameter.aidl
index 0993627..9484008 100644
--- a/media/aidl/android/media/soundtrigger_middleware/ModelParameter.aidl
+++ b/media/aidl/android/media/soundtrigger/ModelParameter.aidl
@@ -13,13 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
/**
* Model specific parameters to be used with parameter set and get APIs.
*
* {@hide}
*/
+@VintfStability
@Backing(type="int")
enum ModelParameter {
/**
diff --git a/media/aidl/android/media/soundtrigger_middleware/ModelParameterRange.aidl b/media/aidl/android/media/soundtrigger/ModelParameterRange.aidl
similarity index 89%
rename from media/aidl/android/media/soundtrigger_middleware/ModelParameterRange.aidl
rename to media/aidl/android/media/soundtrigger/ModelParameterRange.aidl
index d6948a8..e7c616b 100644
--- a/media/aidl/android/media/soundtrigger_middleware/ModelParameterRange.aidl
+++ b/media/aidl/android/media/soundtrigger/ModelParameterRange.aidl
@@ -13,13 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
/**
* Value range for a model parameter.
*
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
+@VintfStability
parcelable ModelParameterRange {
/** Minimum (inclusive) */
int minInclusive;
diff --git a/media/aidl/android/media/soundtrigger_middleware/Phrase.aidl b/media/aidl/android/media/soundtrigger/Phrase.aidl
similarity index 91%
rename from media/aidl/android/media/soundtrigger_middleware/Phrase.aidl
rename to media/aidl/android/media/soundtrigger/Phrase.aidl
index 98a489f8..077db21 100644
--- a/media/aidl/android/media/soundtrigger_middleware/Phrase.aidl
+++ b/media/aidl/android/media/soundtrigger/Phrase.aidl
@@ -13,13 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
/**
* Key phrase descriptor.
*
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
+@VintfStability
parcelable Phrase {
/** Unique keyphrase ID assigned at enrollment time. */
int id;
diff --git a/media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl b/media/aidl/android/media/soundtrigger/PhraseRecognitionEvent.aidl
similarity index 82%
rename from media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl
rename to media/aidl/android/media/soundtrigger/PhraseRecognitionEvent.aidl
index 6a3ec61..654f7c2 100644
--- a/media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl
+++ b/media/aidl/android/media/soundtrigger/PhraseRecognitionEvent.aidl
@@ -13,16 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
-import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
-import android.media.soundtrigger_middleware.RecognitionEvent;
+import android.media.soundtrigger.PhraseRecognitionExtra;
+import android.media.soundtrigger.RecognitionEvent;
/**
* An event that gets sent to indicate a phrase recognition (or aborting of the recognition
process).
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
+@VintfStability
parcelable PhraseRecognitionEvent {
/** Common recognition event. */
RecognitionEvent common;
diff --git a/media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl b/media/aidl/android/media/soundtrigger/PhraseRecognitionExtra.aidl
similarity index 69%
rename from media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl
rename to media/aidl/android/media/soundtrigger/PhraseRecognitionExtra.aidl
index cb96bf3..eb523eb 100644
--- a/media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl
+++ b/media/aidl/android/media/soundtrigger/PhraseRecognitionExtra.aidl
@@ -13,23 +13,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
-import android.media.soundtrigger_middleware.ConfidenceLevel;
+import android.media.soundtrigger.ConfidenceLevel;
/**
* Specialized recognition event for key phrase detection.
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
+@VintfStability
parcelable PhraseRecognitionExtra {
- // TODO(ytai): Constants / enums.
-
- /** keyphrase ID */
+ /** Keyphrase ID */
int id;
- /** recognition modes used for this keyphrase */
+ /** Bitfield, indexed by RecognitionMode. */
int recognitionModes;
- /** confidence level for mode RECOGNITION_MODE_VOICE_TRIGGER */
+ /** Confidence level for mode RECOGNITION_MODE_VOICE_TRIGGER. Value is between 0-100. */
int confidenceLevel;
- /** number of user confidence levels */
+ /** Number of user confidence levels */
ConfidenceLevel[] levels;
}
diff --git a/media/aidl/android/media/soundtrigger_middleware/PhraseSoundModel.aidl b/media/aidl/android/media/soundtrigger/PhraseSoundModel.aidl
similarity index 84%
rename from media/aidl/android/media/soundtrigger_middleware/PhraseSoundModel.aidl
rename to media/aidl/android/media/soundtrigger/PhraseSoundModel.aidl
index 81028c1..e0ffdee 100644
--- a/media/aidl/android/media/soundtrigger_middleware/PhraseSoundModel.aidl
+++ b/media/aidl/android/media/soundtrigger/PhraseSoundModel.aidl
@@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
-import android.media.soundtrigger_middleware.SoundModel;
-import android.media.soundtrigger_middleware.Phrase;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Phrase;
/**
* Specialized sound model for key phrase detection.
@@ -24,6 +24,8 @@
* information indicated by phrases field.
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
+@VintfStability
parcelable PhraseSoundModel {
/** Common part of sound model descriptor */
SoundModel common;
diff --git a/media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl b/media/aidl/android/media/soundtrigger/Properties.aidl
similarity index 92%
rename from media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl
rename to media/aidl/android/media/soundtrigger/Properties.aidl
index 9c56e7b..efa1b6a 100644
--- a/media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl
+++ b/media/aidl/android/media/soundtrigger/Properties.aidl
@@ -13,13 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
/**
* Capabilities of a sound trigger module.
* {@hide}
*/
-parcelable SoundTriggerModuleProperties {
+@JavaDerive(equals = true, toString = true)
+@VintfStability
+parcelable Properties {
/** Implementor name */
String implementor;
/** Implementation description */
@@ -44,7 +46,7 @@
int maxKeyPhrases;
/** Maximum number of concurrent users detected */
int maxUsers;
- /** All supported modes. e.g RecognitionMode.VOICE_TRIGGER */
+ /** All supported modes. Bitfield, indexed by RecognitionMode. */
int recognitionModes;
/** Supports seamless transition from detection to capture */
boolean captureTransition;
diff --git a/media/aidl/android/media/soundtrigger_middleware/RecognitionConfig.aidl b/media/aidl/android/media/soundtrigger/RecognitionConfig.aidl
similarity index 82%
rename from media/aidl/android/media/soundtrigger_middleware/RecognitionConfig.aidl
rename to media/aidl/android/media/soundtrigger/RecognitionConfig.aidl
index 5c0eeb1..a00f0e5 100644
--- a/media/aidl/android/media/soundtrigger_middleware/RecognitionConfig.aidl
+++ b/media/aidl/android/media/soundtrigger/RecognitionConfig.aidl
@@ -13,14 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
-import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
+import android.media.soundtrigger.PhraseRecognitionExtra;
/**
* Configuration for tuning behavior of an active recognition process.
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
+@VintfStability
parcelable RecognitionConfig {
/* Capture and buffer audio for this recognition instance. */
boolean captureRequested;
@@ -34,6 +36,6 @@
*/
int audioCapabilities;
- /** Opaque capture configuration data. */
+ /** Capture configuration data. Content is implementation-defined. */
byte[] data;
}
diff --git a/media/aidl/android/media/soundtrigger_middleware/RecognitionEvent.aidl b/media/aidl/android/media/soundtrigger/RecognitionEvent.aidl
similarity index 84%
rename from media/aidl/android/media/soundtrigger_middleware/RecognitionEvent.aidl
rename to media/aidl/android/media/soundtrigger/RecognitionEvent.aidl
index a237ec1..94668a3 100644
--- a/media/aidl/android/media/soundtrigger_middleware/RecognitionEvent.aidl
+++ b/media/aidl/android/media/soundtrigger/RecognitionEvent.aidl
@@ -13,25 +13,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
import android.media.audio.common.AudioConfig;
-import android.media.soundtrigger_middleware.RecognitionStatus;
-import android.media.soundtrigger_middleware.SoundModelType;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.SoundModelType;
/**
* An event that gets sent to indicate a recognition (or aborting of the recognition process).
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
+@VintfStability
parcelable RecognitionEvent {
/** Recognition status. */
- RecognitionStatus status;
+ RecognitionStatus status = RecognitionStatus.INVALID;
/** Event type, same as sound model type. */
- SoundModelType type;
+ SoundModelType type = SoundModelType.INVALID;
/** Is it possible to capture audio from this utterance buffered by the implementation. */
boolean captureAvailable;
- /* Audio session ID. framework use. */
- int captureSession;
/**
* Delay in ms between end of model detection and start of audio available for capture.
* A negative value is possible (e.g. if key phrase is also available for Capture.
diff --git a/media/aidl/android/media/soundtrigger_middleware/RecognitionMode.aidl b/media/aidl/android/media/soundtrigger/RecognitionMode.aidl
similarity index 94%
rename from media/aidl/android/media/soundtrigger_middleware/RecognitionMode.aidl
rename to media/aidl/android/media/soundtrigger/RecognitionMode.aidl
index d8bfff4..ce2cffe 100644
--- a/media/aidl/android/media/soundtrigger_middleware/RecognitionMode.aidl
+++ b/media/aidl/android/media/soundtrigger/RecognitionMode.aidl
@@ -13,12 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
/**
* Recognition mode.
* {@hide}
*/
+@VintfStability
@Backing(type="int")
enum RecognitionMode {
/** Simple voice trigger. */
diff --git a/media/aidl/android/media/soundtrigger_middleware/RecognitionStatus.aidl b/media/aidl/android/media/soundtrigger/RecognitionStatus.aidl
similarity index 81%
rename from media/aidl/android/media/soundtrigger_middleware/RecognitionStatus.aidl
rename to media/aidl/android/media/soundtrigger/RecognitionStatus.aidl
index d563edc..cccf0f3 100644
--- a/media/aidl/android/media/soundtrigger_middleware/RecognitionStatus.aidl
+++ b/media/aidl/android/media/soundtrigger/RecognitionStatus.aidl
@@ -13,14 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
/**
* A status for indicating the type of a recognition event.
* {@hide}
*/
+@VintfStability
@Backing(type="int")
enum RecognitionStatus {
+ /**
+ * Used as default value in parcelables to indicate that a value was not set.
+ * Should never be considered a valid setting, except for backward compatibility scenarios.
+ */
+ INVALID = -1,
/** Recognition success. */
SUCCESS = 0,
/** Recognition aborted (e.g. capture preempted by another use-case. */
diff --git a/media/aidl/android/media/soundtrigger_middleware/SoundModel.aidl b/media/aidl/android/media/soundtrigger/SoundModel.aidl
similarity index 85%
rename from media/aidl/android/media/soundtrigger_middleware/SoundModel.aidl
rename to media/aidl/android/media/soundtrigger/SoundModel.aidl
index 8186fb7..94244d0 100644
--- a/media/aidl/android/media/soundtrigger_middleware/SoundModel.aidl
+++ b/media/aidl/android/media/soundtrigger/SoundModel.aidl
@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
-import android.media.soundtrigger_middleware.SoundModelType;
+import android.media.soundtrigger.SoundModelType;
import android.os.ParcelFileDescriptor;
/**
@@ -23,9 +23,11 @@
* aggregation.
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
+@VintfStability
parcelable SoundModel {
/** Model type. */
- SoundModelType type;
+ SoundModelType type = SoundModelType.INVALID;
/** Unique sound model ID. */
String uuid;
/**
diff --git a/media/aidl/android/media/soundtrigger_middleware/SoundModelType.aidl b/media/aidl/android/media/soundtrigger/SoundModelType.aidl
similarity index 75%
rename from media/aidl/android/media/soundtrigger_middleware/SoundModelType.aidl
rename to media/aidl/android/media/soundtrigger/SoundModelType.aidl
index f2abc9a..34a9376 100644
--- a/media/aidl/android/media/soundtrigger_middleware/SoundModelType.aidl
+++ b/media/aidl/android/media/soundtrigger/SoundModelType.aidl
@@ -13,16 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
/**
* Sound model type.
* {@hide}
*/
+@VintfStability
@Backing(type="int")
enum SoundModelType {
- /** Unspecified sound model type */
- UNKNOWN = -1,
+ /**
+ * Used as default value in parcelables to indicate that a value was not set.
+ * Should never be considered a valid setting, except for backward compatibility scenarios.
+ */
+ INVALID = -1,
/** Key phrase sound models */
KEYPHRASE = 0,
/** All models other than keyphrase */
diff --git a/media/aidl/android/media/soundtrigger_middleware/Status.aidl b/media/aidl/android/media/soundtrigger/Status.aidl
similarity index 83%
rename from media/aidl/android/media/soundtrigger_middleware/Status.aidl
rename to media/aidl/android/media/soundtrigger/Status.aidl
index c7623f5..ca1487f 100644
--- a/media/aidl/android/media/soundtrigger_middleware/Status.aidl
+++ b/media/aidl/android/media/soundtrigger/Status.aidl
@@ -13,13 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
/**
* {@hide}
*/
+@VintfStability
@Backing(type="int")
enum Status {
+ /**
+ * Used as default value in parcelables to indicate that a value was not set.
+ * Should never be considered a valid setting, except for backward compatibility scenarios.
+ */
+ INVALID = -1,
/** Success. */
SUCCESS = 0,
/** Failure due to resource contention. This is typically a temporary condition. */
diff --git a/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl b/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl
index 726af76..6092ac5 100644
--- a/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl
@@ -15,8 +15,8 @@
*/
package android.media.soundtrigger_middleware;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.PhraseRecognitionEvent;
/**
* Main interface for a client to get notifications of events coming from this module.
@@ -28,22 +28,29 @@
* Invoked whenever a recognition event is triggered (typically, on recognition, but also in
* case of external aborting of a recognition or a forced recognition event - see the status
* code in the event for determining).
+ * In case of abortion, the caller may retry after the next onRecognitionAvailabilityChange()
+ * callback.
*/
- void onRecognition(int modelHandle, in RecognitionEvent event);
+ void onRecognition(int modelHandle, in RecognitionEvent event, int captureSession);
/**
* Invoked whenever a phrase recognition event is triggered (typically, on recognition, but
* also in case of external aborting of a recognition or a forced recognition event - see the
* status code in the event for determining).
+ * In case of abortion, the caller may retry after the next onRecognitionAvailabilityChange()
+ * callback.
*/
- void onPhraseRecognition(int modelHandle, in PhraseRecognitionEvent event);
+ void onPhraseRecognition(int modelHandle, in PhraseRecognitionEvent event, int captureSession);
/**
- * Notifies the client the recognition has become available after previously having been
- * unavailable, or vice versa. This method will always be invoked once immediately after
- * attachment, and then every time there is a change in availability.
- * When availability changes from available to unavailable, all active recognitions are aborted,
- * and this event will be sent in addition to the abort event.
+ * Notifies the client that some start/load operations that have previously failed for resource
+ * reasons (threw a ServiceSpecificException(RESOURCE_CONTENTION) or have been preempted) may
+ * now succeed. This is not a guarantee, but a hint for the client to retry.
*/
- void onRecognitionAvailabilityChange(boolean available);
+ void onResourcesAvailable();
+ /**
+ * Notifies the client that a model had been preemptively unloaded by the service.
+ * The caller may retry after the next onRecognitionAvailabilityChange() callback.
+ */
+ void onModelUnloaded(int modelHandle);
/**
* Notifies the client that the associated module has crashed and restarted. The module instance
* is no longer usable and will throw a ServiceSpecificException with a Status.DEAD_OBJECT code
diff --git a/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl b/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl
index c4a5785..0b46fd4 100644
--- a/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl
@@ -15,11 +15,11 @@
*/
package android.media.soundtrigger_middleware;
-import android.media.soundtrigger_middleware.ModelParameter;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.SoundModel;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
+import android.media.soundtrigger.ModelParameter;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.RecognitionConfig;
/**
* A sound-trigger module.
@@ -75,6 +75,9 @@
* Once a recognition event is passed to the client, the recognition automatically become
* inactive, unless the event is of the RecognitionStatus.FORCED kind. Client can also shut down
* the recognition explicitly, via stopRecognition.
+ *
+ * May throw a ServiceSpecificException with an RESOURCE_CONTENTION status to indicate that
+ * resources required for starting the model are currently consumed by other clients.
*/
void startRecognition(int modelHandle, in RecognitionConfig config);
diff --git a/media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl b/media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl
index 667135f..6c210bf 100644
--- a/media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl
@@ -15,17 +15,18 @@
*/
package android.media.soundtrigger_middleware;
-import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
+import android.media.soundtrigger.Properties;
/**
* A descriptor of an available sound trigger module, containing the handle used to reference the
* module, as well its capabilities.
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
parcelable SoundTriggerModuleDescriptor {
/** Module handle to be used for attaching to it. */
int handle;
/** Module capabilities. */
- SoundTriggerModuleProperties properties;
+ Properties properties;
}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioChannelMask.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioChannelMask.aidl
new file mode 100644
index 0000000..c3af3bf
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioChannelMask.aidl
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */// This file has been semi-automatically generated using hidl2aidl from its counterpart in
+// hardware/interfaces/audio/common/5.0/types.hal
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum AudioChannelMask {
+ REPRESENTATION_POSITION = 0,
+ REPRESENTATION_INDEX = 2,
+ NONE = 0,
+ INVALID = -1073741824,
+ OUT_FRONT_LEFT = 1,
+ OUT_FRONT_RIGHT = 2,
+ OUT_FRONT_CENTER = 4,
+ OUT_LOW_FREQUENCY = 8,
+ OUT_BACK_LEFT = 16,
+ OUT_BACK_RIGHT = 32,
+ OUT_FRONT_LEFT_OF_CENTER = 64,
+ OUT_FRONT_RIGHT_OF_CENTER = 128,
+ OUT_BACK_CENTER = 256,
+ OUT_SIDE_LEFT = 512,
+ OUT_SIDE_RIGHT = 1024,
+ OUT_TOP_CENTER = 2048,
+ OUT_TOP_FRONT_LEFT = 4096,
+ OUT_TOP_FRONT_CENTER = 8192,
+ OUT_TOP_FRONT_RIGHT = 16384,
+ OUT_TOP_BACK_LEFT = 32768,
+ OUT_TOP_BACK_CENTER = 65536,
+ OUT_TOP_BACK_RIGHT = 131072,
+ OUT_TOP_SIDE_LEFT = 262144,
+ OUT_TOP_SIDE_RIGHT = 524288,
+ OUT_HAPTIC_A = 536870912,
+ OUT_HAPTIC_B = 268435456,
+ OUT_MONO = 1,
+ OUT_STEREO = 3,
+ OUT_2POINT1 = 11,
+ OUT_2POINT0POINT2 = 786435,
+ OUT_2POINT1POINT2 = 786443,
+ OUT_3POINT0POINT2 = 786439,
+ OUT_3POINT1POINT2 = 786447,
+ OUT_QUAD = 51,
+ OUT_QUAD_BACK = 51,
+ OUT_QUAD_SIDE = 1539,
+ OUT_SURROUND = 263,
+ OUT_PENTA = 55,
+ OUT_5POINT1 = 63,
+ OUT_5POINT1_BACK = 63,
+ OUT_5POINT1_SIDE = 1551,
+ OUT_5POINT1POINT2 = 786495,
+ OUT_5POINT1POINT4 = 184383,
+ OUT_6POINT1 = 319,
+ OUT_7POINT1 = 1599,
+ OUT_7POINT1POINT2 = 788031,
+ OUT_7POINT1POINT4 = 185919,
+ OUT_MONO_HAPTIC_A = 536870913,
+ OUT_STEREO_HAPTIC_A = 536870915,
+ OUT_HAPTIC_AB = 805306368,
+ OUT_MONO_HAPTIC_AB = 805306369,
+ OUT_STEREO_HAPTIC_AB = 805306371,
+ IN_LEFT = 4,
+ IN_RIGHT = 8,
+ IN_FRONT = 16,
+ IN_BACK = 32,
+ IN_LEFT_PROCESSED = 64,
+ IN_RIGHT_PROCESSED = 128,
+ IN_FRONT_PROCESSED = 256,
+ IN_BACK_PROCESSED = 512,
+ IN_PRESSURE = 1024,
+ IN_X_AXIS = 2048,
+ IN_Y_AXIS = 4096,
+ IN_Z_AXIS = 8192,
+ IN_BACK_LEFT = 65536,
+ IN_BACK_RIGHT = 131072,
+ IN_CENTER = 262144,
+ IN_LOW_FREQUENCY = 1048576,
+ IN_TOP_LEFT = 2097152,
+ IN_TOP_RIGHT = 4194304,
+ IN_VOICE_UPLINK = 16384,
+ IN_VOICE_DNLINK = 32768,
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioConfig.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioConfig.aidl
new file mode 100644
index 0000000..c11eb76
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioConfig.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */// This file has been semi-automatically generated using hidl2aidl from its counterpart in
+// hardware/interfaces/audio/common/5.0/types.hal
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable AudioConfig {
+ int sampleRateHz;
+ int channelMask;
+ android.media.audio.common.AudioFormat format = android.media.audio.common.AudioFormat.INVALID;
+ android.media.audio.common.AudioOffloadInfo offloadInfo;
+ long frameCount;
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioFormat.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioFormat.aidl
new file mode 100644
index 0000000..b7c8659
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioFormat.aidl
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */// This file has been semi-automatically generated using hidl2aidl from its counterpart in
+// hardware/interfaces/audio/common/5.0/types.hal
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum AudioFormat {
+ INVALID = -1,
+ DEFAULT = 0,
+ PCM = 0,
+ MP3 = 16777216,
+ AMR_NB = 33554432,
+ AMR_WB = 50331648,
+ AAC = 67108864,
+ HE_AAC_V1 = 83886080,
+ HE_AAC_V2 = 100663296,
+ VORBIS = 117440512,
+ OPUS = 134217728,
+ AC3 = 150994944,
+ E_AC3 = 167772160,
+ DTS = 184549376,
+ DTS_HD = 201326592,
+ IEC61937 = 218103808,
+ DOLBY_TRUEHD = 234881024,
+ EVRC = 268435456,
+ EVRCB = 285212672,
+ EVRCWB = 301989888,
+ EVRCNW = 318767104,
+ AAC_ADIF = 335544320,
+ WMA = 352321536,
+ WMA_PRO = 369098752,
+ AMR_WB_PLUS = 385875968,
+ MP2 = 402653184,
+ QCELP = 419430400,
+ DSD = 436207616,
+ FLAC = 452984832,
+ ALAC = 469762048,
+ APE = 486539264,
+ AAC_ADTS = 503316480,
+ SBC = 520093696,
+ APTX = 536870912,
+ APTX_HD = 553648128,
+ AC4 = 570425344,
+ LDAC = 587202560,
+ MAT = 603979776,
+ AAC_LATM = 620756992,
+ CELT = 637534208,
+ APTX_ADAPTIVE = 654311424,
+ LHDC = 671088640,
+ LHDC_LL = 687865856,
+ APTX_TWSP = 704643072,
+ MAIN_MASK = -16777216,
+ SUB_MASK = 16777215,
+ PCM_SUB_16_BIT = 1,
+ PCM_SUB_8_BIT = 2,
+ PCM_SUB_32_BIT = 3,
+ PCM_SUB_8_24_BIT = 4,
+ PCM_SUB_FLOAT = 5,
+ PCM_SUB_24_BIT_PACKED = 6,
+ MP3_SUB_NONE = 0,
+ AMR_SUB_NONE = 0,
+ AAC_SUB_MAIN = 1,
+ AAC_SUB_LC = 2,
+ AAC_SUB_SSR = 4,
+ AAC_SUB_LTP = 8,
+ AAC_SUB_HE_V1 = 16,
+ AAC_SUB_SCALABLE = 32,
+ AAC_SUB_ERLC = 64,
+ AAC_SUB_LD = 128,
+ AAC_SUB_HE_V2 = 256,
+ AAC_SUB_ELD = 512,
+ AAC_SUB_XHE = 768,
+ VORBIS_SUB_NONE = 0,
+ E_AC3_SUB_JOC = 1,
+ MAT_SUB_1_0 = 1,
+ MAT_SUB_2_0 = 2,
+ MAT_SUB_2_1 = 3,
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOffloadInfo.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOffloadInfo.aidl
new file mode 100644
index 0000000..b5d889e
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOffloadInfo.aidl
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */// This file has been semi-automatically generated using hidl2aidl from its counterpart in
+// hardware/interfaces/audio/common/5.0/types.hal
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable AudioOffloadInfo {
+ int sampleRateHz;
+ int channelMask;
+ android.media.audio.common.AudioFormat format = android.media.audio.common.AudioFormat.INVALID;
+ android.media.audio.common.AudioStreamType streamType = android.media.audio.common.AudioStreamType.INVALID;
+ int bitRatePerSecond;
+ long durationMicroseconds;
+ boolean hasVideo;
+ boolean isStreaming;
+ int bitWidth;
+ int bufferSize;
+ android.media.audio.common.AudioUsage usage = android.media.audio.common.AudioUsage.INVALID;
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioStreamType.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioStreamType.aidl
new file mode 100644
index 0000000..915c668
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioStreamType.aidl
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */// This file has been semi-automatically generated using hidl2aidl from its counterpart in
+// hardware/interfaces/audio/common/5.0/types.hal
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum AudioStreamType {
+ INVALID = -2,
+ DEFAULT = -1,
+ MIN = 0,
+ VOICE_CALL = 0,
+ SYSTEM = 1,
+ RING = 2,
+ MUSIC = 3,
+ ALARM = 4,
+ NOTIFICATION = 5,
+ BLUETOOTH_SCO = 6,
+ ENFORCED_AUDIBLE = 7,
+ DTMF = 8,
+ TTS = 9,
+ ACCESSIBILITY = 10,
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioUsage.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioUsage.aidl
new file mode 100644
index 0000000..f5130a4
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioUsage.aidl
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */// This file has been semi-automatically generated using hidl2aidl from its counterpart in
+// hardware/interfaces/audio/common/5.0/types.hal
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum AudioUsage {
+ INVALID = -1,
+ UNKNOWN = 0,
+ MEDIA = 1,
+ VOICE_COMMUNICATION = 2,
+ VOICE_COMMUNICATION_SIGNALLING = 3,
+ ALARM = 4,
+ NOTIFICATION = 5,
+ NOTIFICATION_TELEPHONY_RINGTONE = 6,
+ ASSISTANCE_ACCESSIBILITY = 11,
+ ASSISTANCE_NAVIGATION_GUIDANCE = 12,
+ ASSISTANCE_SONIFICATION = 13,
+ GAME = 14,
+ VIRTUAL_SOURCE = 15,
+ ASSISTANT = 16,
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/AudioCapabilities.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/AudioCapabilities.aidl
new file mode 100644
index 0000000..5d88305
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/AudioCapabilities.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum AudioCapabilities {
+ ECHO_CANCELLATION = 1,
+ NOISE_SUPPRESSION = 2,
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ConfidenceLevel.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ConfidenceLevel.aidl
new file mode 100644
index 0000000..5127a11
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ConfidenceLevel.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable ConfidenceLevel {
+ int userId;
+ int levelPercent;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ModelParameter.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ModelParameter.aidl
new file mode 100644
index 0000000..aadbf80
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ModelParameter.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum ModelParameter {
+ INVALID = -1,
+ THRESHOLD_FACTOR = 0,
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ModelParameterRange.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ModelParameterRange.aidl
new file mode 100644
index 0000000..f29b728
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ModelParameterRange.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable ModelParameterRange {
+ int minInclusive;
+ int maxInclusive;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Phrase.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Phrase.aidl
new file mode 100644
index 0000000..11029ba
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Phrase.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable Phrase {
+ int id;
+ int recognitionModes;
+ int[] users;
+ String locale;
+ String text;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseRecognitionEvent.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseRecognitionEvent.aidl
new file mode 100644
index 0000000..b75d1b8
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseRecognitionEvent.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable PhraseRecognitionEvent {
+ android.media.soundtrigger.RecognitionEvent common;
+ android.media.soundtrigger.PhraseRecognitionExtra[] phraseExtras;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseRecognitionExtra.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseRecognitionExtra.aidl
new file mode 100644
index 0000000..e417c69
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseRecognitionExtra.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable PhraseRecognitionExtra {
+ int id;
+ int recognitionModes;
+ int confidenceLevel;
+ android.media.soundtrigger.ConfidenceLevel[] levels;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseSoundModel.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseSoundModel.aidl
new file mode 100644
index 0000000..b4b3854
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseSoundModel.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable PhraseSoundModel {
+ android.media.soundtrigger.SoundModel common;
+ android.media.soundtrigger.Phrase[] phrases;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Properties.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Properties.aidl
new file mode 100644
index 0000000..068db52
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Properties.aidl
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable Properties {
+ String implementor;
+ String description;
+ int version;
+ String uuid;
+ String supportedModelArch;
+ int maxSoundModels;
+ int maxKeyPhrases;
+ int maxUsers;
+ int recognitionModes;
+ boolean captureTransition;
+ int maxBufferMs;
+ boolean concurrentCapture;
+ boolean triggerInEvent;
+ int powerConsumptionMw;
+ int audioCapabilities;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionConfig.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionConfig.aidl
new file mode 100644
index 0000000..63cd2cbb
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionConfig.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable RecognitionConfig {
+ boolean captureRequested;
+ android.media.soundtrigger.PhraseRecognitionExtra[] phraseRecognitionExtras;
+ int audioCapabilities;
+ byte[] data;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionEvent.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionEvent.aidl
new file mode 100644
index 0000000..e6cfb6b
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionEvent.aidl
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable RecognitionEvent {
+ android.media.soundtrigger.RecognitionStatus status = android.media.soundtrigger.RecognitionStatus.INVALID;
+ android.media.soundtrigger.SoundModelType type = android.media.soundtrigger.SoundModelType.INVALID;
+ boolean captureAvailable;
+ int captureDelayMs;
+ int capturePreambleMs;
+ boolean triggerInData;
+ @nullable android.media.audio.common.AudioConfig audioConfig;
+ byte[] data;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionMode.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionMode.aidl
new file mode 100644
index 0000000..5882910
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionMode.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum RecognitionMode {
+ VOICE_TRIGGER = 1,
+ USER_IDENTIFICATION = 2,
+ USER_AUTHENTICATION = 4,
+ GENERIC_TRIGGER = 8,
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionStatus.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionStatus.aidl
new file mode 100644
index 0000000..7881c28
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionStatus.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum RecognitionStatus {
+ INVALID = -1,
+ SUCCESS = 0,
+ ABORTED = 1,
+ FAILURE = 2,
+ FORCED = 3,
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/SoundModel.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/SoundModel.aidl
new file mode 100644
index 0000000..fe38264
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/SoundModel.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable SoundModel {
+ android.media.soundtrigger.SoundModelType type = android.media.soundtrigger.SoundModelType.INVALID;
+ String uuid;
+ String vendorUuid;
+ @nullable ParcelFileDescriptor data;
+ int dataSize;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/SoundModelType.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/SoundModelType.aidl
new file mode 100644
index 0000000..ac78641
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/SoundModelType.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum SoundModelType {
+ INVALID = -1,
+ KEYPHRASE = 0,
+ GENERIC = 1,
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Status.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Status.aidl
new file mode 100644
index 0000000..29f3167
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Status.aidl
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum Status {
+ INVALID = -1,
+ SUCCESS = 0,
+ RESOURCE_CONTENTION = 1,
+ OPERATION_NOT_SUPPORTED = 2,
+ TEMPORARY_PERMISSION_DENIED = 3,
+ DEAD_OBJECT = 4,
+ INTERNAL_ERROR = 5,
+}
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index a031b4c..9e657a9 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -334,6 +334,14 @@
};
/**
+ * @hide
+ */
+ @TestApi
+ public static int[] getSdkUsages() {
+ return SDK_USAGES;
+ }
+
+ /**
* Flag defining a behavior where the audibility of the sound will be ensured by the system.
*/
public final static int FLAG_AUDIBILITY_ENFORCED = 0x1 << 0;
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index b7ea14e..c144195 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -387,6 +387,18 @@
*/
@Deprecated public static final int NUM_STREAMS = AudioSystem.NUM_STREAMS;
+ /** @hide */
+ private static final int[] PUBLIC_STREAM_TYPES = { AudioManager.STREAM_VOICE_CALL,
+ AudioManager.STREAM_SYSTEM, AudioManager.STREAM_RING, AudioManager.STREAM_MUSIC,
+ AudioManager.STREAM_ALARM, AudioManager.STREAM_NOTIFICATION,
+ AudioManager.STREAM_DTMF, AudioManager.STREAM_ACCESSIBILITY };
+
+ /** @hide */
+ @TestApi
+ public static final int[] getPublicStreamTypes() {
+ return PUBLIC_STREAM_TYPES;
+ }
+
/**
* Increase the ringer volume.
*
@@ -1071,6 +1083,7 @@
* @return The minimum valid volume index for the stream.
* @see #getStreamVolume(int)
*/
+ @TestApi
public int getStreamMinVolumeInt(int streamType) {
final IAudioService service = getService();
try {
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 8012f03..321db4e 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -2051,7 +2051,8 @@
};
/** @hide */
- public static String streamToString(int stream) {
+ @TestApi
+ public static @NonNull String streamToString(int stream) {
if (stream >= 0 && stream < STREAM_NAMES.length) return STREAM_NAMES[stream];
if (stream == AudioManager.USE_DEFAULT_STREAM_TYPE) return "USE_DEFAULT_STREAM_TYPE";
return "UNKNOWN_STREAM_" + stream;
diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java
index 1616c03..bac44ad 100644
--- a/media/java/android/media/Image.java
+++ b/media/java/android/media/Image.java
@@ -399,7 +399,7 @@
* <p>The number and meaning of the planes in an Image are determined by the
* format of the Image.</p>
*
- * <p>Once the Image has been closed, any access to the the plane's
+ * <p>Once the Image has been closed, any access to the plane's
* ByteBuffer will fail.</p>
*
* @see #getFormat
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index cc05ecd..521cbcb 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -1695,7 +1695,6 @@
private static final int CB_ERROR = 3;
private static final int CB_OUTPUT_FORMAT_CHANGE = 4;
-
private class EventHandler extends Handler {
private MediaCodec mCodec;
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 4f761ba..ccd830a 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -19,7 +19,6 @@
import static android.Manifest.permission.BIND_IMS_SERVICE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -4308,7 +4307,7 @@
@RequiresPermission(BIND_IMS_SERVICE)
public void setOnRtpRxNoticeListener(
@NonNull Context context,
- @NonNull @CallbackExecutor Executor executor,
+ @NonNull Executor executor,
@NonNull OnRtpRxNoticeListener listener) {
Objects.requireNonNull(context);
Preconditions.checkArgument(
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 1efd4c6..da18a77 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -171,8 +171,12 @@
}
/**
+ *
* Sets the {@link LogSessionId} for MediaRecorder.
*
+ * <p>The log session ID is a random 32-byte hexadecimal string that is used for monitoring the
+ * MediaRecorder performance.</p>
+ *
* @param id the global ID for monitoring the MediaRecorder performance
*/
public void setLogSessionId(@NonNull LogSessionId id) {
diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java
index 0c73348..d00e5b5 100644
--- a/media/java/android/media/audiopolicy/AudioMix.java
+++ b/media/java/android/media/audiopolicy/AudioMix.java
@@ -113,12 +113,10 @@
*/
public static final int MIX_TYPE_INVALID = -1;
/**
- * @hide
* Mix type indicating playback streams are mixed.
*/
public static final int MIX_TYPE_PLAYERS = 0;
/**
- * @hide
* Mix type indicating recording streams are mixed.
*/
public static final int MIX_TYPE_RECORDERS = 1;
diff --git a/media/java/android/media/audiopolicy/AudioMixingRule.java b/media/java/android/media/audiopolicy/AudioMixingRule.java
index abbcc66..99ddff2 100644
--- a/media/java/android/media/audiopolicy/AudioMixingRule.java
+++ b/media/java/android/media/audiopolicy/AudioMixingRule.java
@@ -16,6 +16,9 @@
package android.media.audiopolicy;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -24,6 +27,7 @@
import android.os.Parcel;
import android.util.Log;
+import java.lang.annotation.Retention;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Objects;
@@ -199,7 +203,19 @@
}
private final int mTargetMixType;
- int getTargetMixType() { return mTargetMixType; }
+
+ /** @hide */
+ @IntDef({AudioMix.MIX_TYPE_PLAYERS, AudioMix.MIX_TYPE_RECORDERS})
+ @Retention(SOURCE)
+ public @interface MixType {}
+
+ /**
+ * Gets target mix type of the mixing rule.
+ */
+ public @MixType int getTargetMixType() {
+ return mTargetMixType;
+ }
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private final ArrayList<AudioMixMatchCriterion> mCriteria;
/** @hide */
@@ -469,17 +485,24 @@
}
/**
- * Set target mix type of the mixing rule.
+ * Sets target mix type of the mixing rule.
*
- * <p>Note: If the mix type was not specified, it will be decided automatically by mixing
- * rule. For {@link #RULE_MATCH_UID}, the default type is {@link AudioMix#MIX_TYPE_PLAYERS}.
+ * <p>Note: If the mix type was not specified, it will be decided automatically by matched
+ * mixing rule. For example, {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_USAGE} or {@link
+ * AudioMixingRule#RULE_MATCH_USERID} applied {@link AudioMix#MIX_TYPE_PLAYERS}, {@link
+ * AudioMixingRule#RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET} applied {@link
+ * AudioMix#MIX_TYPE_RECORDERS}. For {@link AudioMixingRule#RULE_MATCH_UID}, the mix type
+ * could be {@link AudioMix#MIX_TYPE_PLAYERS} or {@link AudioMix#MIX_TYPE_RECORDERS}, and
+ * {@link AudioMix#MIX_TYPE_PLAYERS} is the default value.
*
* @param mixType {@link AudioMix#MIX_TYPE_PLAYERS} or {@link AudioMix#MIX_TYPE_RECORDERS}
* @return the same Builder instance.
- *
- * @hide
*/
- public @NonNull Builder setTargetMixType(int mixType) {
+ public @NonNull Builder setTargetMixType(@MixType int mixType) {
+ if (mixType != AudioMix.MIX_TYPE_PLAYERS && mixType != AudioMix.MIX_TYPE_RECORDERS) {
+ throw new IllegalArgumentException("Illegal argument for mix type");
+ }
+
mTargetMixType = mixType;
Log.i("AudioMixingRule", "Builder setTargetMixType " + mixType);
return this;
diff --git a/media/java/android/media/audiopolicy/AudioProductStrategy.java b/media/java/android/media/audiopolicy/AudioProductStrategy.java
index fca3498..31d5967 100644
--- a/media/java/android/media/audiopolicy/AudioProductStrategy.java
+++ b/media/java/android/media/audiopolicy/AudioProductStrategy.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.media.AudioAttributes;
import android.media.AudioSystem;
import android.media.MediaRecorder;
@@ -240,6 +241,7 @@
* @return the legacy stream type relevant for the given {@link AudioAttributes}.
* If none is found, it return DEFAULT stream type.
*/
+ @TestApi
public int getLegacyStreamTypeForAudioAttributes(@NonNull AudioAttributes aa) {
Preconditions.checkNotNull(aa, "AudioAttributes must not be null");
for (final AudioAttributesGroup aag : mAudioAttributesGroups) {
@@ -273,6 +275,7 @@
* @return the volume group id relevant for the given streamType.
* If none is found, {@link AudioVolumeGroup#DEFAULT_VOLUME_GROUP} is returned.
*/
+ @TestApi
public int getVolumeGroupIdForLegacyStreamType(int streamType) {
for (final AudioAttributesGroup aag : mAudioAttributesGroups) {
if (aag.supportsStreamType(streamType)) {
@@ -288,6 +291,7 @@
* @return the volume group id associated with the given audio attributes if found,
* {@link AudioVolumeGroup#DEFAULT_VOLUME_GROUP} otherwise.
*/
+ @TestApi
public int getVolumeGroupIdForAudioAttributes(@NonNull AudioAttributes aa) {
Preconditions.checkNotNull(aa, "AudioAttributes must not be null");
for (final AudioAttributesGroup aag : mAudioAttributesGroups) {
@@ -352,11 +356,19 @@
* @hide
* Default attributes, with default source to be aligned with native.
*/
- public static final @NonNull AudioAttributes sDefaultAttributes =
+ private static final @NonNull AudioAttributes DEFAULT_ATTRIBUTES =
new AudioAttributes.Builder().setCapturePreset(MediaRecorder.AudioSource.DEFAULT)
.build();
/**
+ * @hide
+ */
+ @TestApi
+ public static @NonNull AudioAttributes getDefaultAttributes() {
+ return DEFAULT_ATTRIBUTES;
+ }
+
+ /**
* To avoid duplicating the logic in java and native, we shall make use of
* native API native_get_product_strategies_from_audio_attributes
* Keep in sync with frameworks/av/media/libaudioclient/AudioProductStrategy::attributesMatches
@@ -369,7 +381,7 @@
Preconditions.checkNotNull(attr, "attr must not be null");
String refFormattedTags = TextUtils.join(";", refAttr.getTags());
String cliFormattedTags = TextUtils.join(";", attr.getTags());
- if (refAttr.equals(sDefaultAttributes)) {
+ if (refAttr.equals(DEFAULT_ATTRIBUTES)) {
return false;
}
return ((refAttr.getSystemUsage() == AudioAttributes.USAGE_UNKNOWN)
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 6b329f8..b1baf94 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -23,6 +23,8 @@
import android.annotation.SystemApi;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
import android.graphics.Canvas;
import android.graphics.PorterDuff;
import android.graphics.Rect;
@@ -39,6 +41,7 @@
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
+import android.util.Xml;
import android.view.InputEvent;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -99,6 +102,7 @@
private int mSurfaceWidth;
private int mSurfaceHeight;
private final AttributeSet mAttrs;
+ private final XmlResourceParser mParser;
private final int mDefStyleAttr;
private int mWindowZOrder;
private boolean mUseRequestedSurfaceLayout;
@@ -168,7 +172,16 @@
public TvView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- mAttrs = attrs;
+ int sourceResId = Resources.getAttributeSetSourceResId(attrs);
+ if (sourceResId != Resources.ID_NULL) {
+ Log.d(TAG, "Build local AttributeSet");
+ mParser = context.getResources().getXml(sourceResId);
+ mAttrs = Xml.asAttributeSet(mParser);
+ } else {
+ Log.d(TAG, "Use passed in AttributeSet");
+ mParser = null;
+ mAttrs = attrs;
+ }
mDefStyleAttr = defStyleAttr;
resetSurfaceView();
mTvInputManager = (TvInputManager) getContext().getSystemService(Context.TV_INPUT_SERVICE);
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index ee70714..e8ef464 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -46,6 +46,7 @@
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -112,21 +113,34 @@
/**
* All the info about a connection.
*/
- private class ConnectionRecord implements IBinder.DeathRecipient {
- String pkg;
- int uid;
- int pid;
- Bundle rootHints;
- IMediaBrowserServiceCallbacks callbacks;
- BrowserRoot root;
- HashMap<String, List<Pair<IBinder, Bundle>>> subscriptions = new HashMap<>();
+ private static class ConnectionRecord implements IBinder.DeathRecipient {
+ public final MediaBrowserService service;
+ public final String pkg;
+ public final int pid;
+ public final int uid;
+ public final Bundle rootHints;
+ public final IMediaBrowserServiceCallbacks callbacks;
+ public final BrowserRoot root;
+ public final HashMap<String, List<Pair<IBinder, Bundle>>> subscriptions = new HashMap<>();
+
+ ConnectionRecord(
+ MediaBrowserService service, String pkg, int pid, int uid, Bundle rootHints,
+ IMediaBrowserServiceCallbacks callbacks, BrowserRoot root) {
+ this.service = service;
+ this.pkg = pkg;
+ this.pid = pid;
+ this.uid = uid;
+ this.rootHints = rootHints;
+ this.callbacks = callbacks;
+ this.root = root;
+ }
@Override
public void binderDied() {
- mHandler.post(new Runnable() {
+ service.mHandler.post(new Runnable() {
@Override
public void run() {
- mConnections.remove(callbacks.asBinder());
+ service.mConnections.remove(callbacks.asBinder());
}
});
}
@@ -199,39 +213,46 @@
}
}
- private class ServiceBinder extends IMediaBrowserService.Stub {
+ private static class ServiceBinder extends IMediaBrowserService.Stub {
+ private WeakReference<MediaBrowserService> mService;
+
+ private ServiceBinder(MediaBrowserService service) {
+ mService = new WeakReference(service);
+ }
+
@Override
public void connect(final String pkg, final Bundle rootHints,
final IMediaBrowserServiceCallbacks callbacks) {
+ MediaBrowserService service = mService.get();
+ if (service == null) {
+ return;
+ }
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
- if (!isValidPackage(pkg, uid)) {
+ if (!service.isValidPackage(pkg, uid)) {
throw new IllegalArgumentException("Package/uid mismatch: uid=" + uid
+ " package=" + pkg);
}
- mHandler.post(new Runnable() {
+ service.mHandler.post(new Runnable() {
@Override
public void run() {
final IBinder b = callbacks.asBinder();
// Clear out the old subscriptions. We are getting new ones.
- mConnections.remove(b);
+ service.mConnections.remove(b);
- final ConnectionRecord connection = new ConnectionRecord();
- connection.pkg = pkg;
- connection.pid = pid;
- connection.uid = uid;
- connection.rootHints = rootHints;
- connection.callbacks = callbacks;
-
- mCurConnection = connection;
- connection.root = MediaBrowserService.this.onGetRoot(pkg, uid, rootHints);
- mCurConnection = null;
+ // Temporarily sets a placeholder ConnectionRecord to make
+ // getCurrentBrowserInfo() work in onGetRoot().
+ service.mCurConnection =
+ new ConnectionRecord(
+ service, pkg, pid, uid, rootHints, callbacks, null);
+ BrowserRoot root = service.onGetRoot(pkg, uid, rootHints);
+ service.mCurConnection = null;
// If they didn't return something, don't allow this client.
- if (connection.root == null) {
+ if (root == null) {
Log.i(TAG, "No root for client " + pkg + " from service "
+ getClass().getName());
try {
@@ -242,16 +263,19 @@
}
} else {
try {
- mConnections.put(b, connection);
+ ConnectionRecord connection =
+ new ConnectionRecord(
+ service, pkg, pid, uid, rootHints, callbacks, root);
+ service.mConnections.put(b, connection);
b.linkToDeath(connection, 0);
- if (mSession != null) {
+ if (service.mSession != null) {
callbacks.onConnect(connection.root.getRootId(),
- mSession, connection.root.getExtras());
+ service.mSession, connection.root.getExtras());
}
} catch (RemoteException ex) {
Log.w(TAG, "Calling onConnect() failed. Dropping client. "
+ "pkg=" + pkg);
- mConnections.remove(b);
+ service.mConnections.remove(b);
}
}
}
@@ -260,13 +284,18 @@
@Override
public void disconnect(final IMediaBrowserServiceCallbacks callbacks) {
- mHandler.post(new Runnable() {
+ MediaBrowserService service = mService.get();
+ if (service == null) {
+ return;
+ }
+
+ service.mHandler.post(new Runnable() {
@Override
public void run() {
final IBinder b = callbacks.asBinder();
// Clear out the old subscriptions. We are getting new ones.
- final ConnectionRecord old = mConnections.remove(b);
+ final ConnectionRecord old = service.mConnections.remove(b);
if (old != null) {
// TODO
old.callbacks.asBinder().unlinkToDeath(old, 0);
@@ -283,20 +312,25 @@
@Override
public void addSubscription(final String id, final IBinder token, final Bundle options,
final IMediaBrowserServiceCallbacks callbacks) {
- mHandler.post(new Runnable() {
+ MediaBrowserService service = mService.get();
+ if (service == null) {
+ return;
+ }
+
+ service.mHandler.post(new Runnable() {
@Override
public void run() {
final IBinder b = callbacks.asBinder();
// Get the record for the connection
- final ConnectionRecord connection = mConnections.get(b);
+ final ConnectionRecord connection = service.mConnections.get(b);
if (connection == null) {
Log.w(TAG, "addSubscription for callback that isn't registered id="
+ id);
return;
}
- MediaBrowserService.this.addSubscription(id, connection, token, options);
+ service.addSubscription(id, connection, token, options);
}
});
}
@@ -310,18 +344,23 @@
@Override
public void removeSubscription(final String id, final IBinder token,
final IMediaBrowserServiceCallbacks callbacks) {
- mHandler.post(new Runnable() {
+ MediaBrowserService service = mService.get();
+ if (service == null) {
+ return;
+ }
+
+ service.mHandler.post(new Runnable() {
@Override
public void run() {
final IBinder b = callbacks.asBinder();
- ConnectionRecord connection = mConnections.get(b);
+ ConnectionRecord connection = service.mConnections.get(b);
if (connection == null) {
Log.w(TAG, "removeSubscription for callback that isn't registered id="
+ id);
return;
}
- if (!MediaBrowserService.this.removeSubscription(id, connection, token)) {
+ if (!service.removeSubscription(id, connection, token)) {
Log.w(TAG, "removeSubscription called for " + id
+ " which is not subscribed");
}
@@ -332,16 +371,21 @@
@Override
public void getMediaItem(final String mediaId, final ResultReceiver receiver,
final IMediaBrowserServiceCallbacks callbacks) {
- mHandler.post(new Runnable() {
+ MediaBrowserService service = mService.get();
+ if (service == null) {
+ return;
+ }
+
+ service.mHandler.post(new Runnable() {
@Override
public void run() {
final IBinder b = callbacks.asBinder();
- ConnectionRecord connection = mConnections.get(b);
+ ConnectionRecord connection = service.mConnections.get(b);
if (connection == null) {
Log.w(TAG, "getMediaItem for callback that isn't registered id=" + mediaId);
return;
}
- performLoadItem(mediaId, connection, receiver);
+ service.performLoadItem(mediaId, connection, receiver);
}
});
}
@@ -350,7 +394,7 @@
@Override
public void onCreate() {
super.onCreate();
- mBinder = new ServiceBinder();
+ mBinder = new ServiceBinder(this);
}
@Override
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index 7c5f58e..116237f 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -282,7 +282,6 @@
return status;
}
-
status_t JMediaExtractor::getSampleMeta(sp<MetaData> *sampleMeta) {
return mImpl->getSampleMeta(sampleMeta);
}
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java
index 1131c62..27cf943 100644
--- a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java
@@ -255,7 +255,7 @@
AudioVolumeGroupCallbackHelper vgCbReceiver = new AudioVolumeGroupCallbackHelper();
mAudioManager.registerVolumeGroupCallback(mContext.getMainExecutor(), vgCbReceiver);
- final List<Integer> publicStreams = Ints.asList(PUBLIC_STREAM_TYPES);
+ final List<Integer> publicStreams = Ints.asList(AudioManager.getPublicStreamTypes());
try {
// Validate Audio Volume Groups callback reception
for (final AudioVolumeGroup audioVolumeGroup : audioVolumeGroups) {
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioProductStrategyTest.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioProductStrategyTest.java
index c0f596b..0e918d1 100644
--- a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioProductStrategyTest.java
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioProductStrategyTest.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.assertNotEquals;
import android.media.AudioAttributes;
+import android.media.AudioManager;
import android.media.AudioSystem;
import android.media.audiopolicy.AudioProductStrategy;
import android.media.audiopolicy.AudioVolumeGroup;
@@ -71,7 +72,7 @@
assertNotNull(audioProductStrategies);
assertTrue(audioProductStrategies.size() > 0);
- for (final int streamType : PUBLIC_STREAM_TYPES) {
+ for (final int streamType : AudioManager.getPublicStreamTypes()) {
AudioAttributes aaFromStreamType =
AudioProductStrategy.getAudioAttributesForStrategyWithLegacyStreamType(
streamType);
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumesTestBase.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumesTestBase.java
index a17d65c..b30ef30 100644
--- a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumesTestBase.java
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumesTestBase.java
@@ -37,15 +37,10 @@
// Default matches the invalid (empty) attributes from native.
// The difference is the input source default which is not aligned between native and java
public static final AudioAttributes sDefaultAttributes =
- AudioProductStrategy.sDefaultAttributes;
+ AudioProductStrategy.getDefaultAttributes();
public static final AudioAttributes sInvalidAttributes = new AudioAttributes.Builder().build();
- public final int[] PUBLIC_STREAM_TYPES = { AudioManager.STREAM_VOICE_CALL,
- AudioManager.STREAM_SYSTEM, AudioManager.STREAM_RING, AudioManager.STREAM_MUSIC,
- AudioManager.STREAM_ALARM, AudioManager.STREAM_NOTIFICATION,
- AudioManager.STREAM_DTMF, AudioManager.STREAM_ACCESSIBILITY };
-
public AudioVolumesTestBase() {
super("com.android.audiopolicytest", AudioPolicyTest.class);
}
@@ -63,7 +58,7 @@
}
AudioAttributes avgAttributes = sDefaultAttributes;
for (final AudioAttributes aa : avg.getAudioAttributes()) {
- if (!aa.equals(AudioProductStrategy.sDefaultAttributes)) {
+ if (!aa.equals(AudioProductStrategy.getDefaultAttributes())) {
avgAttributes = aa;
break;
}
@@ -89,7 +84,7 @@
assertTrue(!avg.getAudioAttributes().isEmpty());
AudioAttributes avgAttributes = sDefaultAttributes;
for (final AudioAttributes aa : avg.getAudioAttributes()) {
- if (!aa.equals(AudioProductStrategy.sDefaultAttributes)) {
+ if (!aa.equals(AudioProductStrategy.getDefaultAttributes())) {
avgAttributes = aa;
break;
}
@@ -114,7 +109,7 @@
// Store the original volumes that that they can be recovered in tearDown().
mOriginalStreamVolumes.clear();
- for (int streamType : PUBLIC_STREAM_TYPES) {
+ for (int streamType : AudioManager.getPublicStreamTypes()) {
mOriginalStreamVolumes.put(streamType, mAudioManager.getStreamVolume(streamType));
}
// Store the original volume per attributes so that they can be recovered in tearDown()
diff --git a/packages/CompanionDeviceManager/res/values-af/strings.xml b/packages/CompanionDeviceManager/res/values-af/strings.xml
index cdf4851..3faed55 100644
--- a/packages/CompanionDeviceManager/res/values-af/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-af/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Kies \'n <xliff:g id="PROFILE_NAME">%1$s</xliff:g> om deur <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> bestuur te word"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"toestel"</string>
<string name="profile_name_watch" msgid="576290739483672360">"horlosie"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Stel <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> om jou <xliff:g id="PROFILE_NAME">%2$s</xliff:g> te bestuur – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Jy het <xliff:g id="APP_NAME">%1$s</xliff:g> nodig om jou <xliff:g id="PROFILE_NAME">%2$s</xliff:g> te bestuur. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nee, dankie"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Laat <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> toe om jou <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> te bestuur"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Hierdie program is nodig om jou <xliff:g id="PROFILE_NAME">%1$s</xliff:g> te bestuur. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Laat toe"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Moenie toelaat nie"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-am/strings.xml b/packages/CompanionDeviceManager/res/values-am/strings.xml
index a03ea0df..99466d7 100644
--- a/packages/CompanionDeviceManager/res/values-am/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-am/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"በ<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> የሚተዳደር <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ይምረጡ"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"መሣሪያ"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ሰዓት"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> የእርስዎን <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> እንዲያስተዳድር ያቀናብሩት"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> የእርስዎን <xliff:g id="PROFILE_NAME">%2$s</xliff:g> ለማስተዳደር ያስፈልጋል። <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"አዎ"</string>
- <string name="consent_no" msgid="1335543792857823917">"አይ፣ አመሰግናለሁ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> የእርስዎን <xliff:g id="DEVICE_NAME">%2$s</xliff:g> - <strong></strong> እንዲያስተዳደር ይፍቀዱ"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"የእርስዎን <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ለማስተዳደር ይህ መተግበሪያ ያስፈልጋል <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"ፍቀድ"</string>
+ <string name="consent_no" msgid="2640796915611404382">"አትፍቀድ"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ar/strings.xml b/packages/CompanionDeviceManager/res/values-ar/strings.xml
index 970c46b..c3f1e73 100644
--- a/packages/CompanionDeviceManager/res/values-ar/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ar/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"اختَر <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ليديره تطبيق <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"جهاز"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ساعة"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"اضبط <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> لإدارة <xliff:g id="PROFILE_NAME">%2$s</xliff:g> على <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"يجب توفّر تطبيق <xliff:g id="APP_NAME">%1$s</xliff:g> لإدارة <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"نعم"</string>
- <string name="consent_no" msgid="1335543792857823917">"لا، شكرًا"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"السماح للتطبيق <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> بإدارة <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"هذا التطبيق مطلوب لإدارة <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"السماح"</string>
+ <string name="consent_no" msgid="2640796915611404382">"عدم السماح"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-as/strings.xml b/packages/CompanionDeviceManager/res/values-as/strings.xml
index 477844c..2a2bc25 100644
--- a/packages/CompanionDeviceManager/res/values-as/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-as/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>এ পৰিচালনা কৰিব লগা এটা <xliff:g id="PROFILE_NAME">%1$s</xliff:g> বাছনি কৰক"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"ডিভাইচ"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ঘড়ী"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"আপোনাৰ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> পৰিচালনা কৰিবলৈ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ছেট কৰক - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"আপোনাৰ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> পৰিচালনা কৰিবলৈ <xliff:g id="APP_NAME">%1$s</xliff:g>ৰ আৱশ্যক। <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"হয়"</string>
- <string name="consent_no" msgid="1335543792857823917">"নালাগে, ধন্যবাদ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>ক আপোনাৰ <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> পৰিচালনা কৰিবলৈ দিয়ক"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"আপোনাৰ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> পৰিচালনা কৰিবলৈ এই এপ্টোৰ আৱশ্যক। <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"অনুমতি দিয়ক"</string>
+ <string name="consent_no" msgid="2640796915611404382">"অনুমতি নিদিব"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-az/strings.xml b/packages/CompanionDeviceManager/res/values-az/strings.xml
index f10c639..4710dbe 100644
--- a/packages/CompanionDeviceManager/res/values-az/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-az/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> tərəfindən idarə ediləcək <xliff:g id="PROFILE_NAME">%1$s</xliff:g> seçin"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"cihaz"</string>
<string name="profile_name_watch" msgid="576290739483672360">"izləyin"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> profilinizin <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> tərəfindən idarə olunmasını ayarlayın - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> profilinizi idarə etmək üçün <xliff:g id="APP_NAME">%1$s</xliff:g> tələb olunur. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Bəli"</string>
- <string name="consent_no" msgid="1335543792857823917">"Xeyr, çox sağolun"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> tətbiqinin <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> cihazınızı idarə etməsinə icazə verin"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Bu tətbiq <xliff:g id="PROFILE_NAME">%1$s</xliff:g> profilinizi idarə etmək üçün lazımdır. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"İcazə verin"</string>
+ <string name="consent_no" msgid="2640796915611404382">"İcazə verməyin"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
index e8542f3..d687b05 100644
--- a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Odaberite profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kojim će upravljati aplikacija <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string>
<string name="profile_name_watch" msgid="576290739483672360">"sat"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Podesite aplikaciju <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> da upravlja profilom <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je neophodna za upravljanje profilom <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Da"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ne, hvala"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Dozvolite aplikaciji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> da upravlja uređajem <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Ova aplikacija je potrebna za upravljanje profilom <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Dozvoli"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Ne dozvoli"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-be/strings.xml b/packages/CompanionDeviceManager/res/values-be/strings.xml
index 13be6f2..2236052f5 100644
--- a/packages/CompanionDeviceManager/res/values-be/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-be/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Выберыце прыладу (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), якой будзе кіраваць праграма <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"прылада"</string>
<string name="profile_name_watch" msgid="576290739483672360">"гадзіннік"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Дазвольце праграме <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> кіраваць прыладай \"<xliff:g id="PROFILE_NAME">%2$s</xliff:g>\" – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Для кіравання прыладай \"<xliff:g id="PROFILE_NAME">%2$s</xliff:g>\" патрабуецца праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\". <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Так"</string>
- <string name="consent_no" msgid="1335543792857823917">"Не, дзякуй"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Дазвольце праграме <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> кіраваць прыладай <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Гэта праграма неабходная для кіравання профілем \"<xliff:g id="PROFILE_NAME">%1$s</xliff:g>\". <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Дазволіць"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Не дазваляць"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-bg/strings.xml b/packages/CompanionDeviceManager/res/values-bg/strings.xml
index 3bda5e6..996ca90 100644
--- a/packages/CompanionDeviceManager/res/values-bg/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bg/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Изберете устройство (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), което да се управлява от <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"устройство"</string>
<string name="profile_name_watch" msgid="576290739483672360">"часовник"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Задайте <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> да управлява устройството ви (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"За управление на <xliff:g id="PROFILE_NAME">%2$s</xliff:g> се изисква <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Да"</string>
- <string name="consent_no" msgid="1335543792857823917">"Не, благодаря"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Разрешаване на <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> да управлява устройството ви <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Това приложение е необходимо за управление на <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Разрешаване"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Забраняване"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-bn/strings.xml b/packages/CompanionDeviceManager/res/values-bn/strings.xml
index d3bc515..16d25ce 100644
--- a/packages/CompanionDeviceManager/res/values-bn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bn/strings.xml
@@ -19,9 +19,9 @@
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
<string name="chooser_title" msgid="2262294130493605839">"<xliff:g id="PROFILE_NAME">%1$s</xliff:g> বেছে নিন যেটি <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> ম্যানেজ করবে"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"ডিভাইস"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"দেখুন"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"আপনার <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> ম্যানেজ করার জন্য <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> সেট করুন"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g>-কে আপনার <xliff:g id="PROFILE_NAME">%2$s</xliff:g>.ম্যানেজ করতে দিতে হবে। <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"হ্যাঁ"</string>
- <string name="consent_no" msgid="1335543792857823917">"না থাক"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ঘড়ি"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"আপনার <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ম্যানেজ করার জন্য <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> -কে অনুমতি দিন"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"আপনার <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ম্যানেজ করতে এই অ্যাপটি প্রয়োজন। <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"অনুমতি দিন"</string>
+ <string name="consent_no" msgid="2640796915611404382">"অনুমতি দেবেন না"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-bs/strings.xml b/packages/CompanionDeviceManager/res/values-bs/strings.xml
index 905b306..10f753c 100644
--- a/packages/CompanionDeviceManager/res/values-bs/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bs/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Odaberite uređaj <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kojim će upravljati aplikacija <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string>
<string name="profile_name_watch" msgid="576290739483672360">"sat"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Postavite aplikaciju <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> da upravlja vašim uređajem <xliff:g id="PROFILE_NAME">%2$s</xliff:g> — <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Potrebna je aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> za upravljanje uređajem <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Da"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ne, hvala"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Dozvolite aplikaciji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> da upravlja uređajem <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Ova aplikacija je potrebna za upravljanje profilom: <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Dozvoli"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Nemoj dozvoliti"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ca/strings.xml b/packages/CompanionDeviceManager/res/values-ca/strings.xml
index 86dc694..e55a033 100644
--- a/packages/CompanionDeviceManager/res/values-ca/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ca/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Tria un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> perquè el gestioni <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositiu"</string>
<string name="profile_name_watch" msgid="576290739483672360">"rellotge"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Defineix que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> gestioni el teu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> (<strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>)"</string>
- <string name="profile_summary" msgid="2009764182871566255">"L\'aplicació <xliff:g id="APP_NAME">%1$s</xliff:g> és necessària per gestionar el teu <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Sí"</string>
- <string name="consent_no" msgid="1335543792857823917">"No, gràcies"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Permet que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> gestioni el teu <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Aquesta aplicació es necessita per gestionar el teu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Permet"</string>
+ <string name="consent_no" msgid="2640796915611404382">"No permetis"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-cs/strings.xml b/packages/CompanionDeviceManager/res/values-cs/strings.xml
index 389ccd0..48fbda1 100644
--- a/packages/CompanionDeviceManager/res/values-cs/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-cs/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Vyberte zařízení <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, které chcete spravovat pomocí aplikace <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"zařízení"</string>
<string name="profile_name_watch" msgid="576290739483672360">"hodinky"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Nastavit aplikaci <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ke správě tohoto zařízení: <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Ke správě profilu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> je potřeba aplikace <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ano"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ne, díky"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Povolit aplikaci <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> spravovat <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Tato aplikace je nutná pro správu profilu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Povolit"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Nepovolovat"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-da/strings.xml b/packages/CompanionDeviceManager/res/values-da/strings.xml
index 5a31f9b..446c301 100644
--- a/packages/CompanionDeviceManager/res/values-da/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-da/strings.xml
@@ -16,12 +16,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="4470785958457506021">"Medfølgende enhedshåndtering"</string>
+ <string name="app_label" msgid="4470785958457506021">"Medfølgende enhedsadministrator"</string>
<string name="chooser_title" msgid="2262294130493605839">"Vælg den enhed (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), som skal administreres af <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"enhed"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ur"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Angiv <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> til administration af: <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> er nødvendig for at administrere: <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nej tak"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Tillad at <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> kan administrere: <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Du skal bruge denne app for at administrere dit <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Tillad"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Tillad ikke"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-de/strings.xml b/packages/CompanionDeviceManager/res/values-de/strings.xml
index b643eb2..33d831d 100644
--- a/packages/CompanionDeviceManager/res/values-de/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-de/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Gerät (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) auswählen, das von <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> verwaltet werden soll"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"Gerät"</string>
<string name="profile_name_watch" msgid="576290739483672360">"Smartwatch"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> zum Verwalten deines Geräts (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) festlegen – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> ist erforderlich, um dein Gerät (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) zu verwalten. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nein danke"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> erlauben, dein Gerät <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> zu verwalten"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Diese App wird zur Verwaltung des Profils „<xliff:g id="PROFILE_NAME">%1$s</xliff:g>“ benötigt. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Zulassen"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Nicht zulassen"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-el/strings.xml b/packages/CompanionDeviceManager/res/values-el/strings.xml
index 60de2ff..7a78c06 100644
--- a/packages/CompanionDeviceManager/res/values-el/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-el/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Επιλέξτε ένα προφίλ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> για διαχείριση από την εφαρμογή <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"συσκευή"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ρολόι"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Ορίστε μια εφαρμογή <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> για διαχείριση του προφίλ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> απαιτείται για τη διαχείριση του προφίλ <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ναι"</string>
- <string name="consent_no" msgid="1335543792857823917">"Όχι, ευχαριστώ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Επιτρέψτε στην εφαρμογή <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> να διαχειρίζεται τη συσκευή <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Αυτή η εφαρμογή είναι απαραίτητη για τη διαχείριση του προφίλ σας <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Να επιτρέπεται"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Να μην επιτρέπεται"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
index 2fed1ae..a6ebe65 100644
--- a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
<string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Set <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> is needed to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Yes"</string>
- <string name="consent_no" msgid="1335543792857823917">"No, thanks"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to manage your <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"This app is needed to manage your <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml
index 2fed1ae..a6ebe65 100644
--- a/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
<string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Set <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> is needed to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Yes"</string>
- <string name="consent_no" msgid="1335543792857823917">"No, thanks"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to manage your <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"This app is needed to manage your <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
index 2fed1ae..a6ebe65 100644
--- a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
<string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Set <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> is needed to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Yes"</string>
- <string name="consent_no" msgid="1335543792857823917">"No, thanks"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to manage your <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"This app is needed to manage your <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
index 2fed1ae..a6ebe65 100644
--- a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
<string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Set <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> is needed to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Yes"</string>
- <string name="consent_no" msgid="1335543792857823917">"No, thanks"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to manage your <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"This app is needed to manage your <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml b/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml
index f3c4b1d..6cc56a4 100644
--- a/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
<string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Set <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> is needed to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Yes"</string>
- <string name="consent_no" msgid="1335543792857823917">"No thanks"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to manage your <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"This app is needed to manage your <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Don’t allow"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
index 4fbb57e..dc13159 100644
--- a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Elige un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para que <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> lo administre"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="profile_name_watch" msgid="576290739483672360">"reloj"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Configura <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> para administrar <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Se requiere <xliff:g id="APP_NAME">%1$s</xliff:g> para administrar tu <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Sí"</string>
- <string name="consent_no" msgid="1335543792857823917">"No, gracias"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Permite que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> administre tu <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Esta app es necesaria para administrar tu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
+ <string name="consent_no" msgid="2640796915611404382">"No permitir"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-es/strings.xml b/packages/CompanionDeviceManager/res/values-es/strings.xml
index 5ca9305..7e37c1d 100644
--- a/packages/CompanionDeviceManager/res/values-es/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-es/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Elige un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para gestionarlo con <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="profile_name_watch" msgid="576290739483672360">"reloj"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Haz que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> gestione tu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Se necesita <xliff:g id="APP_NAME">%1$s</xliff:g> para gestionar tu <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Sí"</string>
- <string name="consent_no" msgid="1335543792857823917">"No, gracias"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Permitir que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> gestione tu <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Se necesita esta aplicación para gestionar tu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
+ <string name="consent_no" msgid="2640796915611404382">"No permitir"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-et/strings.xml b/packages/CompanionDeviceManager/res/values-et/strings.xml
index 357f052..399556d 100644
--- a/packages/CompanionDeviceManager/res/values-et/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-et/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Valige seade <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, mida haldab rakendus <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"seade"</string>
<string name="profile_name_watch" msgid="576290739483672360">"käekell"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Määrake rakendus <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> haldama teie seadet <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> on vajalik teie seadme <xliff:g id="PROFILE_NAME">%2$s</xliff:g> haldamiseks. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Jah"</string>
- <string name="consent_no" msgid="1335543792857823917">"Tänan, ei"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Lubage rakendusel <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> hallata teie seadet <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Seda rakendust on vaja teie profiili <xliff:g id="PROFILE_NAME">%1$s</xliff:g> haldamiseks. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Luba"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Ära luba"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-eu/strings.xml b/packages/CompanionDeviceManager/res/values-eu/strings.xml
index 14c7154..764505e 100644
--- a/packages/CompanionDeviceManager/res/values-eu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-eu/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Aukeratu <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> aplikazioak kudeatu beharreko <xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"gailua"</string>
<string name="profile_name_watch" msgid="576290739483672360">"erlojua"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Konfiguratu <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> <xliff:g id="PROFILE_NAME">%2$s</xliff:g> (<strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>) kudea dezan"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> erabili behar duzu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> kudeatzeko. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Bai"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ez"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Eman <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> kudeatzeko baimena <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aplikazioari"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Aplikazioa <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kudeatzeko beharrezkoa da. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Eman baimena"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Ez eman baimenik"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-fa/strings.xml b/packages/CompanionDeviceManager/res/values-fa/strings.xml
index 6bb9620..07d04aa 100644
--- a/packages/CompanionDeviceManager/res/values-fa/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fa/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"انتخاب <xliff:g id="PROFILE_NAME">%1$s</xliff:g> برای مدیریت کردن با <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"دستگاه"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ساعت"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"تنظیم <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> برای مدیریت کردن <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"برای مدیریت کردن <xliff:g id="PROFILE_NAME">%2$s</xliff:g> به <xliff:g id="APP_NAME">%1$s</xliff:g> نیاز دارید. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"بله"</string>
- <string name="consent_no" msgid="1335543792857823917">"نه متشکرم"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"مجاز کردن <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> برای مدیریت کردن <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"این برنامه برای مدیریت <xliff:g id="PROFILE_NAME">%1$s</xliff:g> شما لازم است. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"مجاز بودن"</string>
+ <string name="consent_no" msgid="2640796915611404382">"مجاز نبودن"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-fi/strings.xml b/packages/CompanionDeviceManager/res/values-fi/strings.xml
index 5a9c1cd..528d16c 100644
--- a/packages/CompanionDeviceManager/res/values-fi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fi/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Valitse <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, jota <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> hallinnoi"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"laite"</string>
<string name="profile_name_watch" msgid="576290739483672360">"kello"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Aseta <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> profiilin (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) hallinnoijaksi – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> tarvitaan profiilin (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) hallinnointiin. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Kyllä"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ei kiitos"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Salli, että <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> voi hallinnoida tätä laitettasi: <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Profiilin (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) ylläpitoon tarvitaan tätä sovellusta. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Salli"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Älä salli"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
index b31babd..2cf872c 100644
--- a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Choisissez un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> qui sera géré par <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"appareil"</string>
<string name="profile_name_watch" msgid="576290739483672360">"montre"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Utiliser <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> pour gérer votre <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"L\'application <xliff:g id="APP_NAME">%1$s</xliff:g> est requise pour gérer votre <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Oui"</string>
- <string name="consent_no" msgid="1335543792857823917">"Non merci"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Autoriser <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> pour gérer votre <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Cette application est nécessaire pour gérer votre <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Autoriser"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Ne pas autoriser"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-fr/strings.xml b/packages/CompanionDeviceManager/res/values-fr/strings.xml
index 08c93a2c..ba2fc8e 100644
--- a/packages/CompanionDeviceManager/res/values-fr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fr/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Sélectionner le/la <xliff:g id="PROFILE_NAME">%1$s</xliff:g> qui sera géré(e) par <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"appareil"</string>
<string name="profile_name_watch" msgid="576290739483672360">"montre"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Définir <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> pour la gestion de votre <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> est requis pour gérer votre <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Oui"</string>
- <string name="consent_no" msgid="1335543792857823917">"Non, merci"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Autoriser <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> à gérer votre <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Cette appli est nécessaire pour gérer votre <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Autoriser"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Ne pas autoriser"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-gl/strings.xml b/packages/CompanionDeviceManager/res/values-gl/strings.xml
index c95b90e..5f9a8d7 100644
--- a/packages/CompanionDeviceManager/res/values-gl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-gl/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Escolle un perfil (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) para que o xestione a aplicación <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="profile_name_watch" msgid="576290739483672360">"reloxo"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Define a aplicación <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> para a xestión do teu perfil (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>): <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Necesítase a aplicación <xliff:g id="APP_NAME">%1$s</xliff:g> para xestionar o teu perfil (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>). <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Si"</string>
- <string name="consent_no" msgid="1335543792857823917">"Non, grazas"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Permitir que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> xestione o teu dispositivo (<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>)"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Esta aplicación é necesaria para xestionar o teu perfil (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>). <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Non permitir"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-gu/strings.xml b/packages/CompanionDeviceManager/res/values-gu/strings.xml
index 7e41042..71cf012 100644
--- a/packages/CompanionDeviceManager/res/values-gu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-gu/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> દ્વારા મેનેજ કરવા માટે કોઈ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> પસંદ કરો"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"ડિવાઇસ"</string>
<string name="profile_name_watch" msgid="576290739483672360">"સ્માર્ટવૉચ"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"તમારા <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>ને મેનેજ કરવા માટે <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> સેટ કરો"</string>
- <string name="profile_summary" msgid="2009764182871566255">"તમારા <xliff:g id="PROFILE_NAME">%2$s</xliff:g>ને મેનેજ કરવા માટે <xliff:g id="APP_NAME">%1$s</xliff:g> જરૂરી છે. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"હા"</string>
- <string name="consent_no" msgid="1335543792857823917">"ના, આભાર"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"તમારા <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>ને મેનેજ કરવા માટે <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>ને મંજૂર કરો"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"તમારી <xliff:g id="PROFILE_NAME">%1$s</xliff:g> મેનેજ કરવા માટે આ ઍપ જરૂરી છે. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"મંજૂરી આપો"</string>
+ <string name="consent_no" msgid="2640796915611404382">"મંજૂરી આપશો નહીં"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-hi/strings.xml b/packages/CompanionDeviceManager/res/values-hi/strings.xml
index ac95cc6..d4dd1cb 100644
--- a/packages/CompanionDeviceManager/res/values-hi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hi/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"कोई <xliff:g id="PROFILE_NAME">%1$s</xliff:g> चुनें, ताकि उसे <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> की मदद से प्रबंधित किया जा सके"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"डिवाइस"</string>
<string name="profile_name_watch" msgid="576290739483672360">"स्मार्टवॉच"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"अपने <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> को प्रबंधित करने के लिए, <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> को सेट करें"</string>
- <string name="profile_summary" msgid="2009764182871566255">"आपके <xliff:g id="PROFILE_NAME">%2$s</xliff:g> को प्रबंधित करने के लिए, <xliff:g id="APP_NAME">%1$s</xliff:g> की ज़रूरत है. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"हां"</string>
- <string name="consent_no" msgid="1335543792857823917">"नहीं, रहने दें"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> को, अपनी <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> मैनेज करने की अनुमति दें"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"<xliff:g id="PROFILE_NAME">%1$s</xliff:g> को मैनेज करने के लिए, यह ऐप्लिकेशन ज़रूरी है. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"अनुमति दें"</string>
+ <string name="consent_no" msgid="2640796915611404382">"अनुमति न दें"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-hr/strings.xml b/packages/CompanionDeviceManager/res/values-hr/strings.xml
index df8451f..87c5ae2 100644
--- a/packages/CompanionDeviceManager/res/values-hr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hr/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Odaberite profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kojim će upravljati aplikacija <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string>
<string name="profile_name_watch" msgid="576290739483672360">"satom"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Postavite aplikaciju <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> da upravlja vašim profilom <xliff:g id="PROFILE_NAME">%2$s</xliff:g> –- <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> potrebna je za upravljanje vašim <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Da"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ne, hvala"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Dopustite aplikaciji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> da upravlja vašim <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Ta je aplikacija potrebna za upravljanje vašim profilom <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Dopusti"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Nemoj dopustiti"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-hu/strings.xml b/packages/CompanionDeviceManager/res/values-hu/strings.xml
index ff1c6c5..c7ceb38 100644
--- a/packages/CompanionDeviceManager/res/values-hu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hu/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"A(z) <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> alkalmazással kezelni kívánt <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kiválasztása"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"eszköz"</string>
<string name="profile_name_watch" msgid="576290739483672360">"óra"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"A(z) <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> alkalmazás beállítása a(z) <xliff:g id="PROFILE_NAME">%2$s</xliff:g> (<strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>) kezelésére"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Szükség van a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazásra a(z) <xliff:g id="PROFILE_NAME">%2$s</xliff:g> kezeléséhez. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Igen"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nem"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"A(z) <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> engedélyezése a(z) <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> kezelésére"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Szükség van erre az alkalmazásra a következő kezeléséhez: <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Engedélyezés"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Tiltás"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-hy/strings.xml b/packages/CompanionDeviceManager/res/values-hy/strings.xml
index 194223d..26f7990 100644
--- a/packages/CompanionDeviceManager/res/values-hy/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hy/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Ընտրեք <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ը, որը պետք է կառավարվի <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> հավելվածի կողմից"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"սարք"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ժամացույց"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Ընտրեք <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> հավելվածը որպես <xliff:g id="PROFILE_NAME">%2$s</xliff:g>ի կառավարիչ․ <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Ձեր <xliff:g id="PROFILE_NAME">%2$s</xliff:g>ը կառավարելու համար անհրաժեշտ է <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը։ <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Այո"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ոչ, շնորհակալություն"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Թույլատրեք <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> հավելվածին կառավարել ձեր <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> սարքը"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Այս հավելվածն անհրաժեշտ է <xliff:g id="PROFILE_NAME">%1$s</xliff:g> սարքը կամ պրոֆիլը կառավարելու համար։ <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Թույլատրել"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Չթույլատրել"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-in/strings.xml b/packages/CompanionDeviceManager/res/values-in/strings.xml
index 58bf3cb..b0618d4 100644
--- a/packages/CompanionDeviceManager/res/values-in/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-in/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Pilih <xliff:g id="PROFILE_NAME">%1$s</xliff:g> untuk dikelola oleh <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"perangkat"</string>
<string name="profile_name_watch" msgid="576290739483672360">"smartwatch"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Tetapkan <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> untuk mengelola <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Perlu <xliff:g id="APP_NAME">%1$s</xliff:g> untuk mengelola <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ya"</string>
- <string name="consent_no" msgid="1335543792857823917">"Tidak"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Izinkan <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> untuk mengelola <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Aplikasi ini diperlukan untuk mengelola <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Izinkan"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Jangan izinkan"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-is/strings.xml b/packages/CompanionDeviceManager/res/values-is/strings.xml
index cc5b989..b7d7c6a 100644
--- a/packages/CompanionDeviceManager/res/values-is/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-is/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Velja <xliff:g id="PROFILE_NAME">%1$s</xliff:g> sem <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> á að stjórna"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"tæki"</string>
<string name="profile_name_watch" msgid="576290739483672360">"úr"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Veita <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> stjórn á <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> er krafist til að stjórna <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Já"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nei, takk"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Veita <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> stjórn á: <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Þetta forrit er nauðsynlegt til að hafa umsjón með <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Leyfa"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Ekki leyfa"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-it/strings.xml b/packages/CompanionDeviceManager/res/values-it/strings.xml
index 4cbefd8..ce003e7 100644
--- a/packages/CompanionDeviceManager/res/values-it/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-it/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Scegli un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> che sia gestito da <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="profile_name_watch" msgid="576290739483672360">"orologio"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Configura <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> per gestire il tuo <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"È richiesta l\'app <xliff:g id="APP_NAME">%1$s</xliff:g> per gestire il tuo <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Sì"</string>
- <string name="consent_no" msgid="1335543792857823917">"No, grazie"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Consenti all\'app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> di gestire <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Questa app è necessaria per gestire il tuo <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Consenti"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Non consentire"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-iw/strings.xml b/packages/CompanionDeviceManager/res/values-iw/strings.xml
index 8663e56..54c523c 100644
--- a/packages/CompanionDeviceManager/res/values-iw/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-iw/strings.xml
@@ -17,11 +17,11 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"ניהול מכשיר מותאם"</string>
- <string name="chooser_title" msgid="2262294130493605839">"בחירה של <xliff:g id="PROFILE_NAME">%1$s</xliff:g> לניהול באמצעות <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="chooser_title" msgid="2262294130493605839">"בחירת <xliff:g id="PROFILE_NAME">%1$s</xliff:g> לניהול באמצעות <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"מכשיר"</string>
<string name="profile_name_watch" msgid="576290739483672360">"שעון"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"הגדרה של <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> לניהול <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> נדרשת לניהול של <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"כן"</string>
- <string name="consent_no" msgid="1335543792857823917">"לא תודה"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"אישור לאפליקציה <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> לנהל את <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"האפליקציה הזו נחוצה כדי לנהל את ה<xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"יש אישור"</string>
+ <string name="consent_no" msgid="2640796915611404382">"אין אישור"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ja/strings.xml b/packages/CompanionDeviceManager/res/values-ja/strings.xml
index ca17336..f92fafe 100644
--- a/packages/CompanionDeviceManager/res/values-ja/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ja/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> の管理対象となる<xliff:g id="PROFILE_NAME">%1$s</xliff:g>の選択"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"デバイス"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ウォッチ"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> で <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> を管理するよう設定する"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> を管理するために <xliff:g id="APP_NAME">%1$s</xliff:g> が必要です。<xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"はい"</string>
- <string name="consent_no" msgid="1335543792857823917">"いいえ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> に <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> の管理を許可する"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"このアプリは <xliff:g id="PROFILE_NAME">%1$s</xliff:g> の管理に必要です。<xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"許可"</string>
+ <string name="consent_no" msgid="2640796915611404382">"許可しない"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ka/strings.xml b/packages/CompanionDeviceManager/res/values-ka/strings.xml
index 300c94f..34efdd2 100644
--- a/packages/CompanionDeviceManager/res/values-ka/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ka/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"აირჩიეთ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, რომელიც უნდა მართოს <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>-მა"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"მოწყობილობა"</string>
<string name="profile_name_watch" msgid="576290739483672360">"საათი"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"დააყენეთ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>, რომ მართოს თქვენი <xliff:g id="PROFILE_NAME">%2$s</xliff:g> — <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"თქვენი <xliff:g id="PROFILE_NAME">%2$s</xliff:g>-ის სამართავად საჭიროა <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"დიახ"</string>
- <string name="consent_no" msgid="1335543792857823917">"არა, გმადლობთ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"ნება დართეთ <strong><xliff:g id="APP_NAME">%1$s</xliff:g>-ს</strong>, რომ მართოს თქვენი <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"ეს აპი საჭიროა თქვენი <xliff:g id="PROFILE_NAME">%1$s</xliff:g>-ს სამართავად. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"დაშვება"</string>
+ <string name="consent_no" msgid="2640796915611404382">"არ დაიშვას"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-kk/strings.xml b/packages/CompanionDeviceManager/res/values-kk/strings.xml
index 94d6c3e..3c7f697 100644
--- a/packages/CompanionDeviceManager/res/values-kk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-kk/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> арқылы басқарылатын <xliff:g id="PROFILE_NAME">%1$s</xliff:g> құрылғысын таңдаңыз"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"құрылғы"</string>
<string name="profile_name_watch" msgid="576290739483672360">"сағат"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> қолданбасына <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) құрылғысын басқаруға рұқсат беру"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> құрылғысын басқару үшін <xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасы керек. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Иә"</string>
- <string name="consent_no" msgid="1335543792857823917">"Жоқ, рақмет"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> қолданбасына <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> құрылғысын басқаруға рұқсат беру"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Бұл қолданба <xliff:g id="PROFILE_NAME">%1$s</xliff:g> профиліңізді басқару үшін қажет. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Рұқсат беру"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Рұқсат бермеу"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-km/strings.xml b/packages/CompanionDeviceManager/res/values-km/strings.xml
index db13fe7..74ccd84 100644
--- a/packages/CompanionDeviceManager/res/values-km/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-km/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"ជ្រើសរើស <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ដើម្បីឱ្យស្ថិតក្រោមការគ្រប់គ្រងរបស់ <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"ឧបករណ៍"</string>
<string name="profile_name_watch" msgid="576290739483672360">"នាឡិកា"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"កំណត់ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ដើម្បីគ្រប់គ្រង <xliff:g id="PROFILE_NAME">%2$s</xliff:g> របស់អ្នក - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"ចាំបាច់ត្រូវមាន <xliff:g id="APP_NAME">%1$s</xliff:g> ដើម្បីគ្រប់គ្រង <xliff:g id="PROFILE_NAME">%2$s</xliff:g> របស់អ្នក។ <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"បាទ/ចាស"</string>
- <string name="consent_no" msgid="1335543792857823917">"ទេ អរគុណ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"អនុញ្ញាតឱ្យ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> គ្រប់គ្រង <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> របស់អ្នក"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"ត្រូវការកម្មវិធីនេះ ដើម្បីគ្រប់គ្រង <xliff:g id="PROFILE_NAME">%1$s</xliff:g> របស់អ្នក។ <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"អនុញ្ញាត"</string>
+ <string name="consent_no" msgid="2640796915611404382">"កុំអនុញ្ញាត"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-kn/strings.xml b/packages/CompanionDeviceManager/res/values-kn/strings.xml
index 0225166..2a82d1f 100644
--- a/packages/CompanionDeviceManager/res/values-kn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-kn/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> ಮೂಲಕ ನಿರ್ವಹಿಸಬೇಕಾದ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ಅನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"ಸಾಧನ"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ವೀಕ್ಷಿಸಿ"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"ನಿಮ್ಮ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> ಅನ್ನು ನಿರ್ವಹಿಸಲು, <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ಅನ್ನು ನಿರ್ವಹಿಸಿ"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> ಅನ್ನು ನಿರ್ವಹಿಸಲು, <xliff:g id="APP_NAME">%1$s</xliff:g> ಅಗತ್ಯವಿದೆ. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"ಹೌದು"</string>
- <string name="consent_no" msgid="1335543792857823917">"ಬೇಡ, ಧನ್ಯವಾದಗಳು"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"ನಿಮ್ಮ <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ಅನ್ನು ನಿರ್ವಹಿಸಲು <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ಅನ್ನು ಅನುಮತಿಸಿ"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"ನಿಮ್ಮ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. ಅನ್ನು ನಿರ್ವಹಿಸಲು ಈ ಆ್ಯಪ್ನ ಅಗತ್ಯವಿದೆ. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"ಅನುಮತಿಸಿ"</string>
+ <string name="consent_no" msgid="2640796915611404382">"ಅನುಮತಿಸಬೇಡಿ"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ko/strings.xml b/packages/CompanionDeviceManager/res/values-ko/strings.xml
index 1363e57..6ca01bf 100644
--- a/packages/CompanionDeviceManager/res/values-ko/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ko/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>에서 관리할 <xliff:g id="PROFILE_NAME">%1$s</xliff:g>을(를) 선택"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"기기"</string>
<string name="profile_name_watch" msgid="576290739483672360">"시계"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>에서 <xliff:g id="PROFILE_NAME">%2$s</xliff:g>(<strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>)을(를) 관리하도록 설정"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> 프로필을 관리하려면 <xliff:g id="APP_NAME">%1$s</xliff:g> 앱이 필요합니다. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"예"</string>
- <string name="consent_no" msgid="1335543792857823917">"취소"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>에서 <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> 기기를 관리하도록 허용"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"이 앱은 <xliff:g id="PROFILE_NAME">%1$s</xliff:g> 프로필을 관리하는 데 필요합니다. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"허용"</string>
+ <string name="consent_no" msgid="2640796915611404382">"허용 안함"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ky/strings.xml b/packages/CompanionDeviceManager/res/values-ky/strings.xml
index c01e235..18d38a8 100644
--- a/packages/CompanionDeviceManager/res/values-ky/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ky/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"<xliff:g id="PROFILE_NAME">%1$s</xliff:g> <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> тарабынан башкарылсын"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"түзмөк"</string>
<string name="profile_name_watch" msgid="576290739483672360">"саат"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> түзмөгүңүздү башкарсын"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> профилиңизди башкаруу үчүн <xliff:g id="APP_NAME">%1$s</xliff:g> керек. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ооба"</string>
- <string name="consent_no" msgid="1335543792857823917">"Жок, рахмат"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> колдонмосуна <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> түзмөгүңүздү башкарууга уруксат бериңиз"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Бул колдонмо <xliff:g id="PROFILE_NAME">%1$s</xliff:g> профилиңизди башкаруу үчүн керек. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Уруксат берүү"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Уруксат берилбесин"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-lo/strings.xml b/packages/CompanionDeviceManager/res/values-lo/strings.xml
index 68218dd..a1eb713 100644
--- a/packages/CompanionDeviceManager/res/values-lo/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lo/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"ເລືອກ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ເພື່ອໃຫ້ຖືກຈັດການໂດຍ <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"ອຸປະກອນ"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ໂມງ"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"ຕັ້ງ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ເພື່ອຈັດການ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> ຂອງທ່ານ"</string>
- <string name="profile_summary" msgid="2009764182871566255">"ຕ້ອງໃຊ້ <xliff:g id="APP_NAME">%1$s</xliff:g> ເພື່ອຈັດການ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> ຂອງທ່ານ. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"ແມ່ນແລ້ວ"</string>
- <string name="consent_no" msgid="1335543792857823917">"ບໍ່, ຂອບໃຈ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"ອະນຸຍາດໃຫ້ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ຈັດການ <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ຂອງທ່ານໄດ້"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"ຕ້ອງໃຊ້ແອັບນີ້ເພື່ອຈັດການ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ຂອງທ່ານ. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"ອະນຸຍາດ"</string>
+ <string name="consent_no" msgid="2640796915611404382">"ບໍ່ອະນຸຍາດ"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-lt/strings.xml b/packages/CompanionDeviceManager/res/values-lt/strings.xml
index 5fd8280..65f371d 100644
--- a/packages/CompanionDeviceManager/res/values-lt/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lt/strings.xml
@@ -19,9 +19,9 @@
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
<string name="chooser_title" msgid="2262294130493605839">"Jūsų <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, kurį valdys <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> (pasirinkite)"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"įrenginys"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"laikrodis"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Nustatyti, kad <xliff:g id="PROFILE_NAME">%2$s</xliff:g> <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> būtų valdomas programos <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Norint valdyti jūsų <xliff:g id="PROFILE_NAME">%2$s</xliff:g>, reikalinga programa „<xliff:g id="APP_NAME">%1$s</xliff:g>“. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Taip"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ne, ačiū"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"laikrodį"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Leisti <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> tvarkyti <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Ši programa reikalinga norint tvarkyti jūsų <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Leisti"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Neleisti"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-lv/strings.xml b/packages/CompanionDeviceManager/res/values-lv/strings.xml
index bf036ec..b18bfe4 100644
--- a/packages/CompanionDeviceManager/res/values-lv/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lv/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Profila (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) izvēle, ko pārvaldīt lietotnē <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"ierīce"</string>
<string name="profile_name_watch" msgid="576290739483672360">"pulkstenis"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Lietotnes <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> iestatīšana profila (<xliff:g id="PROFILE_NAME">%2$s</xliff:g> — <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>) pārvaldībai"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Lai pārvaldītu profilu (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>), nepieciešama lietotne <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Jā"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nē, paldies"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Atļauja lietotnei <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> pārvaldīt ierīci <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Šī lietotne ir nepieciešama jūsu profila (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) pārvaldībai. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Atļaut"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Neatļaut"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-mk/strings.xml b/packages/CompanionDeviceManager/res/values-mk/strings.xml
index 427ca8f..9d745c4 100644
--- a/packages/CompanionDeviceManager/res/values-mk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mk/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Изберете <xliff:g id="PROFILE_NAME">%1$s</xliff:g> со којшто ќе управува <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"уред"</string>
<string name="profile_name_watch" msgid="576290739483672360">"часовник"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Поставете ја <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> да управува со <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> е потребна за да управува со <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Да"</string>
- <string name="consent_no" msgid="1335543792857823917">"Не, фала"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Дозволете <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> да управува со вашиот <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Апликацијава е потребна за управување со вашиот <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Дозволи"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Не дозволувај"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ml/strings.xml b/packages/CompanionDeviceManager/res/values-ml/strings.xml
index a48c45f..28c88da 100644
--- a/packages/CompanionDeviceManager/res/values-ml/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ml/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> ഉപയോഗിച്ച് മാനേജ് ചെയ്യുന്നതിന് ഒരു <xliff:g id="PROFILE_NAME">%1$s</xliff:g> തിരഞ്ഞെടുക്കുക"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"ഉപകരണം"</string>
<string name="profile_name_watch" msgid="576290739483672360">"വാച്ച്"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"നിങ്ങളുടെ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> മാനേജ് ചെയ്യുന്നതിന് <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> സജ്ജീകരിക്കുക - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> എന്ന ആപ്പിന് നിങ്ങളുടെ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> മാനേജ് ചെയ്യേണ്ടതുണ്ട്. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"വേണം"</string>
- <string name="consent_no" msgid="1335543792857823917">"വേണ്ട"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"നിങ്ങളുടെ <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> മാനേജ് ചെയ്യാൻ, <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> എന്നതിനെ അനുവദിക്കുക"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"നിങ്ങളുടെ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> മാനേജ് ചെയ്യാൻ ഈ ആപ്പ് ആവശ്യമാണ്. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"അനുവദിക്കുക"</string>
+ <string name="consent_no" msgid="2640796915611404382">"അനുവദിക്കരുത്"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-mn/strings.xml b/packages/CompanionDeviceManager/res/values-mn/strings.xml
index 7ac20e6..11e61d9 100644
--- a/packages/CompanionDeviceManager/res/values-mn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mn/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>-н удирдах<xliff:g id="PROFILE_NAME">%1$s</xliff:g>-г сонгоно уу"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"төхөөрөмж"</string>
<string name="profile_name_watch" msgid="576290739483672360">"цаг"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g>-аа удирдахын тулд <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>-г тохируулна уу - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Таны <xliff:g id="PROFILE_NAME">%2$s</xliff:g>-г удирдахын тулд <xliff:g id="APP_NAME">%1$s</xliff:g> шаардлагатай. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Тийм"</string>
- <string name="consent_no" msgid="1335543792857823917">"Үгүй, баярлалаа"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>-г удирдахын тулд <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>-г зөвшөөрнө үү"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Энэ апп таны <xliff:g id="PROFILE_NAME">%1$s</xliff:g>-г удирдахад шаардлагатай. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Зөвшөөрөх"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Бүү зөвшөөр"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-mr/strings.xml b/packages/CompanionDeviceManager/res/values-mr/strings.xml
index 144698b..c73ed28 100644
--- a/packages/CompanionDeviceManager/res/values-mr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mr/strings.xml
@@ -19,9 +19,9 @@
<string name="app_label" msgid="4470785958457506021">"सहयोगी डिव्हाइस व्यवस्थापक"</string>
<string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> द्वारे व्यवस्थापित करण्यासाठी <xliff:g id="PROFILE_NAME">%1$s</xliff:g> निवडा"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"डिव्हाइस"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"पाहा"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"तुमची <xliff:g id="PROFILE_NAME">%2$s</xliff:g> व्यवस्थापित करण्यासाठी <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> सेट करा - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"तुमची <xliff:g id="PROFILE_NAME">%2$s</xliff:g> व्यवस्थापित करण्यासाठी <xliff:g id="APP_NAME">%1$s</xliff:g> आवश्यक आहे. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"होय"</string>
- <string name="consent_no" msgid="1335543792857823917">"नाही, नको"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"वॉच"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"तुमचे <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> व्यवस्थापित करण्यासाठी <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ला अनुमती द्या"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"तुमची <xliff:g id="PROFILE_NAME">%1$s</xliff:g> प्रोफाइल व्यवस्थापित करण्यासाठी हे ॲप आवश्यक आहे. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"अनुमती द्या"</string>
+ <string name="consent_no" msgid="2640796915611404382">"अनुमती देऊ नका"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ms/strings.xml b/packages/CompanionDeviceManager/res/values-ms/strings.xml
index 7bea2c9..d2aebb4 100644
--- a/packages/CompanionDeviceManager/res/values-ms/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ms/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Pilih <xliff:g id="PROFILE_NAME">%1$s</xliff:g> untuk diurus oleh <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"peranti"</string>
<string name="profile_name_watch" msgid="576290739483672360">"jam tangan"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Tetapkan <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> untuk mengurus <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> anda"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> diperlukan untuk mengurus <xliff:g id="PROFILE_NAME">%2$s</xliff:g> anda. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ya"</string>
- <string name="consent_no" msgid="1335543792857823917">"Tidak perlu"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Benarkan <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> untuk mengurus <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> anda"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Apl ini diperlukan untuk mengurus <xliff:g id="PROFILE_NAME">%1$s</xliff:g> anda. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Benarkan"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Jangan benarkan"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-my/strings.xml b/packages/CompanionDeviceManager/res/values-my/strings.xml
index 9c2783c..45c9d8b 100644
--- a/packages/CompanionDeviceManager/res/values-my/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-my/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> က စီမံခန့်ခွဲရန် <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ကို ရွေးချယ်ပါ"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"စက်"</string>
<string name="profile_name_watch" msgid="576290739483672360">"နာရီ"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"သင်၏ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> ကို စီမံခန့်ခွဲရန် <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ကို သတ်မှတ်ပါ"</string>
- <string name="profile_summary" msgid="2009764182871566255">"သင်၏ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> ကို စီမံခန့်ခွဲရန် <xliff:g id="APP_NAME">%1$s</xliff:g> ကို လိုအပ်ပါသည်။ <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Yes"</string>
- <string name="consent_no" msgid="1335543792857823917">"မလိုပါ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"သင်၏ <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ကို စီမံခန့်ခွဲရန် <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ကို ခွင့်ပြုပါ"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"သင်၏ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ကို စီမံခန့်ခွဲရန် ဤအက်ပ်ကိုလိုအပ်သည်။ <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"ခွင့်ပြုရန်"</string>
+ <string name="consent_no" msgid="2640796915611404382">"ခွင့်မပြုပါ"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-nb/strings.xml b/packages/CompanionDeviceManager/res/values-nb/strings.xml
index 26fbb03..af1ffe9 100644
--- a/packages/CompanionDeviceManager/res/values-nb/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-nb/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Velg <xliff:g id="PROFILE_NAME">%1$s</xliff:g> som skal administreres av <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"enhet"</string>
<string name="profile_name_watch" msgid="576290739483672360">"klokke"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Angi <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> for å administrere <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> kreves for å administrere <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nei takk"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Tillat at <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> administrerer <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Denne appen kreves for å administrere <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Tillat"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Ikke tillat"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ne/strings.xml b/packages/CompanionDeviceManager/res/values-ne/strings.xml
index f289b37..b29f94c 100644
--- a/packages/CompanionDeviceManager/res/values-ne/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ne/strings.xml
@@ -16,12 +16,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="4470785958457506021">"सहयोगी यन्त्रको प्रबन्धक"</string>
+ <string name="app_label" msgid="4470785958457506021">"सहयोगी डिभाइसको प्रबन्धक"</string>
<string name="chooser_title" msgid="2262294130493605839">"आफूले <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> प्रयोग गरी व्यवस्थापन गर्न चाहेको <xliff:g id="PROFILE_NAME">%1$s</xliff:g> चयन गर्नुहोस्"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"यन्त्र"</string>
<string name="profile_name_watch" msgid="576290739483672360">"घडी"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"आफ्नो <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> व्यवस्थापन गर्न <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> तोक्नुहोस्"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> व्यवस्थापन गर्न <xliff:g id="APP_NAME">%1$s</xliff:g> इन्स्टल गर्नु पर्ने हुन्छ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"अँ"</string>
- <string name="consent_no" msgid="1335543792857823917">"सहमत छुइनँ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"आफ्नो <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> लाई <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> व्यवस्थापन गर्ने अनुमति दिनुहोस्"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"तपाईंको <xliff:g id="PROFILE_NAME">%1$s</xliff:g> व्यवस्थापन गर्न यो एपलाई अनुमति दिनु पर्ने हुन्छ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"अनुमति दिनुहोस्"</string>
+ <string name="consent_no" msgid="2640796915611404382">"अनुमति नदिनुहोस्"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-nl/strings.xml b/packages/CompanionDeviceManager/res/values-nl/strings.xml
index 0c9cdffd..a56fb9a 100644
--- a/packages/CompanionDeviceManager/res/values-nl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-nl/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Een <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kiezen om te beheren met <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"apparaat"</string>
<string name="profile_name_watch" msgid="576290739483672360">"horloge"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> instellen om je <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> te beheren"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Je hebt <xliff:g id="APP_NAME">%1$s</xliff:g> nodig om je <xliff:g id="PROFILE_NAME">%2$s</xliff:g> te beheren. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nee, bedankt"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> toestaan je <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> te beheren"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Deze app is vereist om je <xliff:g id="PROFILE_NAME">%1$s</xliff:g> te beheren. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Toestaan"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Niet toestaan"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-or/strings.xml b/packages/CompanionDeviceManager/res/values-or/strings.xml
index c8c680f..8e43213 100644
--- a/packages/CompanionDeviceManager/res/values-or/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-or/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> ଦ୍ୱାରା ପରିଚାଳିତ ହେବା ପାଇଁ ଏକ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>କୁ ବାଛନ୍ତୁ"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"ଡିଭାଇସ୍"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ୱାଚ୍"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"ଆପଣଙ୍କ <xliff:g id="PROFILE_NAME">%2$s</xliff:g>କୁ ପରିଚାଳନା କରିବା ପାଇଁ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>କୁ ସେଟ୍ କରନ୍ତୁ - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"ଆପଣଙ୍କ <xliff:g id="PROFILE_NAME">%2$s</xliff:g>କୁ ପରିଚାଳନା କରିବା ପାଇଁ <xliff:g id="APP_NAME">%1$s</xliff:g> ଆବଶ୍ୟକ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"ହଁ"</string>
- <string name="consent_no" msgid="1335543792857823917">"ନା, ଧନ୍ୟବାଦ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"ଆପଣଙ୍କ <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>କୁ ପରିଚାଳନା କରିବା ପାଇଁ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>କୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"ଆପଣଙ୍କ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>କୁ ପରିଚାଳନା କରିବା ପାଇଁ ଏହି ଆପ୍ ଆବଶ୍ୟକ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"ଅନୁମତି ଦିଅନ୍ତୁ"</string>
+ <string name="consent_no" msgid="2640796915611404382">"ଅନୁମତି ଦିଅନ୍ତୁ ନାହିଁ"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-pa/strings.xml b/packages/CompanionDeviceManager/res/values-pa/strings.xml
index 0da9410..54f4f8c 100644
--- a/packages/CompanionDeviceManager/res/values-pa/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pa/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> ਵੱਲੋਂ ਪ੍ਰਬੰਧਿਤ ਕੀਤੇ ਜਾਣ ਲਈ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ਚੁਣੋ"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"ਡੀਵਾਈਸ"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ਸਮਾਰਟ-ਵਾਚ"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ਨੂੰ ਤੁਹਾਡਾ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਲਈ ਸੈੱਟ ਕਰੋ"</string>
- <string name="profile_summary" msgid="2009764182871566255">"ਤੁਹਾਡੇ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਲਈ <xliff:g id="APP_NAME">%1$s</xliff:g> ਦੀ ਲੋੜ ਹੈ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"ਹਾਂ"</string>
- <string name="consent_no" msgid="1335543792857823917">"ਨਹੀਂ ਧੰਨਵਾਦ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ਨੂੰ ਤੁਹਾਡੇ <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"ਤੁਹਾਡੇ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਲਈ ਇਹ ਐਪ ਲੋੜੀਂਦੀ ਹੈ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"ਇਜਾਜ਼ਤ ਦਿਓ"</string>
+ <string name="consent_no" msgid="2640796915611404382">"ਇਜਾਜ਼ਤ ਨਾ ਦਿਓ"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-pl/strings.xml b/packages/CompanionDeviceManager/res/values-pl/strings.xml
index b07af57..a989baa 100644
--- a/packages/CompanionDeviceManager/res/values-pl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pl/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Wybierz profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, którym ma zarządzać aplikacja <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"urządzenie"</string>
<string name="profile_name_watch" msgid="576290739483672360">"zegarek"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Ustaw aplikację <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> do zarządzania profilem <xliff:g id="PROFILE_NAME">%2$s</xliff:g> na urządzeniu <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> jest wymagana do zarządzania profilem <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Tak"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nie, dziękuję"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Zezwól na zarządzanie urządzeniem <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> przez aplikację <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Ta aplikacja jest niezbędna do zarządzania profilem <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Zezwól"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Nie zezwalaj"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
index 16906f6..d2724c0 100644
--- a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Escolha um <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para ser gerenciado pelo app <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="profile_name_watch" msgid="576290739483672360">"relógio"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Defina o app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> como gerenciador do seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> (<strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>)"</string>
- <string name="profile_summary" msgid="2009764182871566255">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> é necessário para gerenciar seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Sim"</string>
- <string name="consent_no" msgid="1335543792857823917">"Agora não"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Permitir que o app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> gerencie seu <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Esse app é necessário para gerenciar seu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Não permitir"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
index 745d163..2f5a53b 100644
--- a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Escolha um <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para ser gerido pela app <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="profile_name_watch" msgid="576290739483672360">"relógio"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Defina a app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> para gerir o seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> é necessária para gerir o seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Sim"</string>
- <string name="consent_no" msgid="1335543792857823917">"Não, obrigado"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Permita que a app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> faça a gestão do seu <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Esta app é necessária para gerir o seu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Não permitir"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-pt/strings.xml b/packages/CompanionDeviceManager/res/values-pt/strings.xml
index 16906f6..d2724c0 100644
--- a/packages/CompanionDeviceManager/res/values-pt/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Escolha um <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para ser gerenciado pelo app <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="profile_name_watch" msgid="576290739483672360">"relógio"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Defina o app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> como gerenciador do seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> (<strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>)"</string>
- <string name="profile_summary" msgid="2009764182871566255">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> é necessário para gerenciar seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Sim"</string>
- <string name="consent_no" msgid="1335543792857823917">"Agora não"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Permitir que o app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> gerencie seu <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Esse app é necessário para gerenciar seu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Não permitir"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ro/strings.xml b/packages/CompanionDeviceManager/res/values-ro/strings.xml
index 187cfbdf..4df74de 100644
--- a/packages/CompanionDeviceManager/res/values-ro/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ro/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Alegeți un profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> pe care să îl gestioneze <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispozitiv"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ceas"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Setați <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> pentru a vă gestiona profilul <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> este necesară pentru a vă gestiona profilul <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Da"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nu, mulțumesc"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Permiteți ca <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> să vă gestioneze dispozitivul <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Această aplicație este necesară pentru a gestiona <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Permiteți"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Nu permiteți"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ru/strings.xml b/packages/CompanionDeviceManager/res/values-ru/strings.xml
index 8dd9a39..ea372d5 100644
--- a/packages/CompanionDeviceManager/res/values-ru/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ru/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Выберите устройство (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), которым будет управлять приложение <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"устройство"</string>
<string name="profile_name_watch" msgid="576290739483672360">"часы"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Разрешите приложению <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> управлять устройством <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>)"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" необходимо для управления устройством (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>). <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Да"</string>
- <string name="consent_no" msgid="1335543792857823917">"Нет"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Разрешите приложению <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> управлять этим устройством: <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Это приложение необходимо для управления вашим профилем \"<xliff:g id="PROFILE_NAME">%1$s</xliff:g>\". <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Разрешить"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Запретить"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-si/strings.xml b/packages/CompanionDeviceManager/res/values-si/strings.xml
index 9e7c02e..a5c2c88 100644
--- a/packages/CompanionDeviceManager/res/values-si/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-si/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> මගින් කළමනාකරණය කරනු ලැබීමට <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ක් තෝරන්න"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"උපාංගය"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ඔරලෝසුව"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ඔබගේ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> කළමනාකරණය කිරීමට සකසන්න - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"ඔබගේ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> කළමනාකරණය කිරීමට <xliff:g id="APP_NAME">%1$s</xliff:g> අවශ්යයි. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"ඔව්"</string>
- <string name="consent_no" msgid="1335543792857823917">"එපා, ස්තුතියි"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> හට ඔබගේ <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> කළමනාකරණය කිරීමට ඉඩ දෙන්න"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"මෙම යෙදුමට ඔබගේ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> කළමනාකරණය කිරීමට අවශ්යයි. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"ඉඩ දෙන්න"</string>
+ <string name="consent_no" msgid="2640796915611404382">"ඉඩ නොදෙන්න"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sk/strings.xml b/packages/CompanionDeviceManager/res/values-sk/strings.xml
index 55a47c2..a9bf77f 100644
--- a/packages/CompanionDeviceManager/res/values-sk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sk/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Vyberte profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, ktorý bude spravovať aplikácia <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"zariadenie"</string>
<string name="profile_name_watch" msgid="576290739483672360">"hodinky"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Nastavte aplikáciu <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>, aby spravovala profil <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Na správu profilu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> je potrebná aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Áno"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nie, vďaka"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Povoliť aplikácii <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> spravovať zariadenie <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Táto aplikácia sa vyžaduje na správu profilu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Povoliť"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Nepovoliť"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sl/strings.xml b/packages/CompanionDeviceManager/res/values-sl/strings.xml
index 159afd5..4eb8f50 100644
--- a/packages/CompanionDeviceManager/res/values-sl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sl/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Izbira naprave <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, ki jo bo upravljala aplikacija <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"naprava"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ura"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Nastavitev aplikacije <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>, ki bo upravljala napravo <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Za upravljanje naprave <xliff:g id="PROFILE_NAME">%2$s</xliff:g> potrebujete aplikacijo <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Da"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ne, hvala"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Aplikaciji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> dovolite upravljanje naprave <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Ta aplikacija je potrebna za upravljanje profila »<xliff:g id="PROFILE_NAME">%1$s</xliff:g>«. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Dovoli"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Ne dovoli"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sq/strings.xml b/packages/CompanionDeviceManager/res/values-sq/strings.xml
index 6fa759c..34357b4 100644
--- a/packages/CompanionDeviceManager/res/values-sq/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sq/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Zgjidh një profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> që do të menaxhohet nga <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"pajisja"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ora inteligjente"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Cakto <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> që të menaxhojë profilin tënd <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Nevojitet <xliff:g id="APP_NAME">%1$s</xliff:g> për të menaxhuar profilin tënd të <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Po"</string>
- <string name="consent_no" msgid="1335543792857823917">"Jo, faleminderit"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Lejo që <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> të menaxhojë pajisjen tënde <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Ky aplikacion nevojitet për të menaxhuar profilin tënd <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Lejo"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Mos lejo"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sr/strings.xml b/packages/CompanionDeviceManager/res/values-sr/strings.xml
index fdbbe8e..37af185 100644
--- a/packages/CompanionDeviceManager/res/values-sr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sr/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Одаберите профил <xliff:g id="PROFILE_NAME">%1$s</xliff:g> којим ће управљати апликација <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"уређај"</string>
<string name="profile_name_watch" msgid="576290739483672360">"сат"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Подесите апликацију <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> да управља профилом <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Апликација <xliff:g id="APP_NAME">%1$s</xliff:g> је неопходна за управљање профилом <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Да"</string>
- <string name="consent_no" msgid="1335543792857823917">"Не, хвала"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Дозволите апликацији <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> да управља уређајем <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Ова апликација је потребна за управљање профилом <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Дозволи"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Не дозволи"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sv/strings.xml b/packages/CompanionDeviceManager/res/values-sv/strings.xml
index bfd2516..f78fadf 100644
--- a/packages/CompanionDeviceManager/res/values-sv/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sv/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Välj en <xliff:g id="PROFILE_NAME">%1$s</xliff:g> för hantering av <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"enhet"</string>
<string name="profile_name_watch" msgid="576290739483672360">"klocka"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Konfigurera <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> för att hantera din <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> krävs för att hantera din <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nej tack"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Tillåt att <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> hanterar din <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Appen behövs för att hantera <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Tillåt"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Tillåt inte"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sw/strings.xml b/packages/CompanionDeviceManager/res/values-sw/strings.xml
index 437ae7f..495c441 100644
--- a/packages/CompanionDeviceManager/res/values-sw/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sw/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Chagua <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ili idhibitiwe na <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"kifaa"</string>
<string name="profile_name_watch" msgid="576290739483672360">"saa"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Weka <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ili udhibiti <xliff:g id="PROFILE_NAME">%2$s</xliff:g> yako - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> linahitajika ili kudhibiti <xliff:g id="PROFILE_NAME">%2$s</xliff:g> yako. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ndiyo"</string>
- <string name="consent_no" msgid="1335543792857823917">"Hapana"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Ruhusu <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> idhibiti <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> yako"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Programu hii inahitajika ili udhibiti wasifu wako wa <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Ruhusu"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Usiruhusu"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ta/strings.xml b/packages/CompanionDeviceManager/res/values-ta/strings.xml
index 9b4a720..20845bd 100644
--- a/packages/CompanionDeviceManager/res/values-ta/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ta/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> ஆப்ஸ் நிர்வகிக்கக்கூடிய <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ஐத் தேர்ந்தெடுங்கள்"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"சாதனம்"</string>
<string name="profile_name_watch" msgid="576290739483672360">"வாட்ச்"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> ஐ நிர்வகிக்க <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ஆப்ஸை அமையுங்கள்"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> ஐ நிர்வகிக்க <xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸ் வேண்டும். <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"ஆம்"</string>
- <string name="consent_no" msgid="1335543792857823917">"வேண்டாம்"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"உங்கள் <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ஐ நிர்வகிக்க <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ஆப்ஸை அனுமதியுங்கள்"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"உங்கள் <xliff:g id="PROFILE_NAME">%1$s</xliff:g> சுயவிவரத்தை நிர்வகிக்க இந்த ஆப்ஸ் தேவைப்படுகிறது. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"அனுமதி"</string>
+ <string name="consent_no" msgid="2640796915611404382">"அனுமதிக்க வேண்டாம்"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-te/strings.xml b/packages/CompanionDeviceManager/res/values-te/strings.xml
index 6e785de..c855cf2 100644
--- a/packages/CompanionDeviceManager/res/values-te/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-te/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> ద్వారా మేనేజ్ చేయబడటానికి ఒక <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ను ఎంచుకోండి"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"పరికరం"</string>
<string name="profile_name_watch" msgid="576290739483672360">"వాచ్"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"మీ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>ను మేనేజ్ చేయడానికి <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>ను సెటప్ చేయండి"</string>
- <string name="profile_summary" msgid="2009764182871566255">"మీ <xliff:g id="PROFILE_NAME">%2$s</xliff:g>ను మేనేజ్ చేయడానికి <xliff:g id="APP_NAME">%1$s</xliff:g> అవసరం ఉంది. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"అవును"</string>
- <string name="consent_no" msgid="1335543792857823917">"వద్దు, ధన్యవాదాలు"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"మీ <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>ను మేనేజ్ చేయడానికి <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>ను అనుమతించండి;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"మీ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ను మేనేజ్ చేయడానికి ఈ యాప్ అవసరం. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"అనుమతించు"</string>
+ <string name="consent_no" msgid="2640796915611404382">"అనుమతించవద్దు"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-th/strings.xml b/packages/CompanionDeviceManager/res/values-th/strings.xml
index b727d42..d78ada2 100644
--- a/packages/CompanionDeviceManager/res/values-th/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-th/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"เลือก<xliff:g id="PROFILE_NAME">%1$s</xliff:g>ที่จะให้มีการจัดการโดย <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"อุปกรณ์"</string>
<string name="profile_name_watch" msgid="576290739483672360">"นาฬิกา"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"ตั้งค่า <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ให้จัดการ<xliff:g id="PROFILE_NAME">%2$s</xliff:g>ของคุณ - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"ต้องใช้ <xliff:g id="APP_NAME">%1$s</xliff:g> ในการจัดการ<xliff:g id="PROFILE_NAME">%2$s</xliff:g> <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"ใช่"</string>
- <string name="consent_no" msgid="1335543792857823917">"ไม่เป็นไร"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"อนุญาตให้ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> จัดการ <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ของคุณ"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"ต้องใช้แอปนี้ในการจัดการ<xliff:g id="PROFILE_NAME">%1$s</xliff:g>ของคุณ <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"อนุญาต"</string>
+ <string name="consent_no" msgid="2640796915611404382">"ไม่อนุญาต"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-tl/strings.xml b/packages/CompanionDeviceManager/res/values-tl/strings.xml
index a93282a..03165b4 100644
--- a/packages/CompanionDeviceManager/res/values-tl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-tl/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Pumili ng <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para pamahalaan ng <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
<string name="profile_name_watch" msgid="576290739483672360">"relo"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Itakda ang <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> para pamahalaan ang iyong <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Kailangan ang <xliff:g id="APP_NAME">%1$s</xliff:g> para pamahalaan ang iyong <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Oo"</string>
- <string name="consent_no" msgid="1335543792857823917">"Huwag na lang"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Payagan ang <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> na pamahalaan ang iyong <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Kinakailangan ang app na ito para pamahalaan ang iyong <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Payagan"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Huwag payagan"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-tr/strings.xml b/packages/CompanionDeviceManager/res/values-tr/strings.xml
index 3abe064..b2c1cf2 100644
--- a/packages/CompanionDeviceManager/res/values-tr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-tr/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> tarafından yönetilecek bir <xliff:g id="PROFILE_NAME">%1$s</xliff:g> seçin"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"cihaz"</string>
<string name="profile_name_watch" msgid="576290739483672360">"saat"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> uygulamasını, <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> cihazınızı yönetecek şekilde ayarlayın"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="PROFILE_NAME">%2$s</xliff:g> yönetimi için gereklidir. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Evet"</string>
- <string name="consent_no" msgid="1335543792857823917">"Hayır, teşekkürler"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> uygulaması <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> cihazınızı yönetebilsin mi?"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Bu uygulama, <xliff:g id="PROFILE_NAME">%1$s</xliff:g> profilinizin yönetilmesi için gereklidir. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"İzin ver"</string>
+ <string name="consent_no" msgid="2640796915611404382">"İzin verme"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-uk/strings.xml b/packages/CompanionDeviceManager/res/values-uk/strings.xml
index 161d95e..61b78e9 100644
--- a/packages/CompanionDeviceManager/res/values-uk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-uk/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Виберіть <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, яким керуватиме додаток <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"пристрій"</string>
<string name="profile_name_watch" msgid="576290739483672360">"годинник"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Налаштуйте додаток <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>, щоб керувати своїм пристроєм <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>)"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Щоб керувати своїм пристроєм (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>), вам потрібен додаток <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Так"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ні, дякую"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Дозволити додатку <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> керувати вашим пристроєм <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Цей додаток потрібен, щоб керувати профілем \"<xliff:g id="PROFILE_NAME">%1$s</xliff:g>\". <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Дозволити"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Не дозволяти"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ur/strings.xml b/packages/CompanionDeviceManager/res/values-ur/strings.xml
index dce1815..ee79921 100644
--- a/packages/CompanionDeviceManager/res/values-ur/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ur/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> کے ذریعے نظم کئے جانے کیلئے <xliff:g id="PROFILE_NAME">%1$s</xliff:g> کو منتخب کریں"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"آلہ"</string>
<string name="profile_name_watch" msgid="576290739483672360">"دیکھیں"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"اپنے <xliff:g id="PROFILE_NAME">%2$s</xliff:g> کا نظم کرنے کے لیے <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> کو سیٹ کریں - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"آپ کے <xliff:g id="PROFILE_NAME">%2$s</xliff:g> کا نظم کرنے کے لیے <xliff:g id="APP_NAME">%1$s</xliff:g> کی ضرورت ہے۔ <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"ہاں"</string>
- <string name="consent_no" msgid="1335543792857823917">"نہیں شکریہ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"اپنے <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> کا نظم کرنے کے لیے <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> کو اجازت دیں"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"اس ایپ کو آپ کے <xliff:g id="PROFILE_NAME">%1$s</xliff:g> کا نظم کرنے کی ضرورت ہے۔ <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"اجازت دیں"</string>
+ <string name="consent_no" msgid="2640796915611404382">"اجازت نہ دیں"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-uz/strings.xml b/packages/CompanionDeviceManager/res/values-uz/strings.xml
index 2ca27b5..7221b6d 100644
--- a/packages/CompanionDeviceManager/res/values-uz/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-uz/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> boshqaradigan <xliff:g id="PROFILE_NAME">%1$s</xliff:g> qurilmasini tanlang"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"qurilma"</string>
<string name="profile_name_watch" msgid="576290739483672360">"soat"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> qurilmalarini boshqarish uchun <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ilovasini sozlang"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> qurilmasini boshqarish uchun <xliff:g id="APP_NAME">%1$s</xliff:g> zarur. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ha"</string>
- <string name="consent_no" msgid="1335543792857823917">"Kerak emas"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> qurilmasini boshqarish uchun <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ilovasiga ruxsat bering"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Bu ilova <xliff:g id="PROFILE_NAME">%1$s</xliff:g> profilini boshqarish uchun kerak. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Ruxsat"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Ruxsat berilmasin"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-vi/strings.xml b/packages/CompanionDeviceManager/res/values-vi/strings.xml
index 06a1ab6..2819e1d 100644
--- a/packages/CompanionDeviceManager/res/values-vi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-vi/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Chọn một <xliff:g id="PROFILE_NAME">%1$s</xliff:g> sẽ do <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> quản lý"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"thiết bị"</string>
<string name="profile_name_watch" msgid="576290739483672360">"đồng hồ"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Đặt <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> để quản lý <xliff:g id="PROFILE_NAME">%2$s</xliff:g> của bạn – <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Cần có <xliff:g id="APP_NAME">%1$s</xliff:g> để quản lý <xliff:g id="PROFILE_NAME">%2$s</xliff:g> của bạn. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Có"</string>
- <string name="consent_no" msgid="1335543792857823917">"Không, cảm ơn"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Cho phép <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> quản lý <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> của bạn"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Cần có ứng dụng này để quản lý <xliff:g id="PROFILE_NAME">%1$s</xliff:g> của bạn. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Cho phép"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Không cho phép"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
index 12bfcf3..1440c40 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"选择要由<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>管理的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"设备"</string>
<string name="profile_name_watch" msgid="576290739483672360">"手表"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"设为由<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>管理您的<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"若要管理<xliff:g id="PROFILE_NAME">%2$s</xliff:g>,您需要使用<xliff:g id="APP_NAME">%1$s</xliff:g>。<xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"好"</string>
- <string name="consent_no" msgid="1335543792857823917">"不用了"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"允许<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>管理您的<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"需要使用此应用,才能管理您的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>。<xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"允许"</string>
+ <string name="consent_no" msgid="2640796915611404382">"不允许"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
index 0c583b2..e3f1eb1 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
@@ -16,12 +16,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="4470785958457506021">"隨附裝置管理員"</string>
+ <string name="app_label" msgid="4470785958457506021">"隨附裝置管理工具"</string>
<string name="chooser_title" msgid="2262294130493605839">"選擇由 <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> 管理的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"裝置"</string>
<string name="profile_name_watch" msgid="576290739483672360">"手錶"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"設定 <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> 來管理您的 <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"必須使用 <xliff:g id="APP_NAME">%1$s</xliff:g> 來管理您的<xliff:g id="PROFILE_NAME">%2$s</xliff:g>。<xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"是"</string>
- <string name="consent_no" msgid="1335543792857823917">"不用了,謝謝"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"允許 <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> 管理您的<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"必須使用此應用程式,才能管理<xliff:g id="PROFILE_NAME">%1$s</xliff:g>。<xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"允許"</string>
+ <string name="consent_no" msgid="2640796915611404382">"不允許"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
index 519f0e8..9f4041d 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"選擇要讓「<xliff:g id="APP_NAME">%2$s</xliff:g>」<strong></strong>管理的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"裝置"</string>
<string name="profile_name_watch" msgid="576290739483672360">"手錶"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"授權讓「<xliff:g id="APP_NAME">%1$s</xliff:g>」<strong></strong>管理你的<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"如要管理你的<xliff:g id="PROFILE_NAME">%2$s</xliff:g>,必須使用「<xliff:g id="APP_NAME">%1$s</xliff:g>」。<xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"是"</string>
- <string name="consent_no" msgid="1335543792857823917">"不用了,謝謝"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」<strong></strong>管理你的「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」<strong></strong>"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"需使用這個應用程式,才能管理「<xliff:g id="PROFILE_NAME">%1$s</xliff:g>」。<xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"允許"</string>
+ <string name="consent_no" msgid="2640796915611404382">"不允許"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-zu/strings.xml b/packages/CompanionDeviceManager/res/values-zu/strings.xml
index 7721b54..dc933ae 100644
--- a/packages/CompanionDeviceManager/res/values-zu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zu/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Khetha i-<xliff:g id="PROFILE_NAME">%1$s</xliff:g> ezophathwa yi-<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"idivayisi"</string>
<string name="profile_name_watch" msgid="576290739483672360">"buka"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Setha i-<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ukuba iphathe i-<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>"</string>
- <string name="profile_summary" msgid="2009764182871566255">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> iyadingeka ukuphatha i-<xliff:g id="PROFILE_NAME">%2$s</xliff:g> yakho. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Yebo"</string>
- <string name="consent_no" msgid="1335543792857823917">"Cha ngiyabonga"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Vumela i-<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ukuthi iphathe i-<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> yakho"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"I-app iyadingeka ukuphatha i-<xliff:g id="PROFILE_NAME">%1$s</xliff:g> yakho. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Vumela"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Ungavumeli"</string>
</resources>
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityAnnotations.java b/packages/Connectivity/framework/src/android/net/ConnectivityAnnotations.java
new file mode 100644
index 0000000..eb1faa0
--- /dev/null
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityAnnotations.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Type annotations for constants used in the connectivity API surface.
+ *
+ * The annotations are maintained in a separate class so that it can be built as
+ * a separate library that other modules can build against, as Typedef should not
+ * be exposed as SystemApi.
+ *
+ * @hide
+ */
+public final class ConnectivityAnnotations {
+ private ConnectivityAnnotations() {}
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, value = {
+ ConnectivityManager.MULTIPATH_PREFERENCE_HANDOVER,
+ ConnectivityManager.MULTIPATH_PREFERENCE_RELIABILITY,
+ ConnectivityManager.MULTIPATH_PREFERENCE_PERFORMANCE,
+ })
+ public @interface MultipathPreference {}
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = false, value = {
+ ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED,
+ ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED,
+ ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED,
+ })
+ public @interface RestrictBackgroundStatus {}
+}
diff --git a/packages/SettingsLib/HelpUtils/src/com/android/settingslib/HelpUtils.java b/packages/SettingsLib/HelpUtils/src/com/android/settingslib/HelpUtils.java
index 541a246..281269e 100644
--- a/packages/SettingsLib/HelpUtils/src/com/android/settingslib/HelpUtils.java
+++ b/packages/SettingsLib/HelpUtils/src/com/android/settingslib/HelpUtils.java
@@ -223,7 +223,7 @@
*
* @return the uri with added query parameters
*/
- private static Uri uriWithAddedParameters(Context context, Uri baseUri) {
+ public static Uri uriWithAddedParameters(Context context, Uri baseUri) {
Uri.Builder builder = baseUri.buildUpon();
// Add in the preferred language
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 8e6e251f..218067d 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -214,6 +214,10 @@
<!-- Default for Settings.System.VIBRATE_WHEN_RINGING -->
<bool name="def_vibrate_when_ringing">false</bool>
+ <!-- Default for Settings.Global.CELL_ON; see PhoneConstants.CELL_ON_FLAG.
+ 0: cellular off; 1: cellular on. -->
+ <integer name="def_cell_on">1</integer>
+
<!-- Default for Settings.Global.APPLY_RAMPING_RINGER -->
<bool name="def_apply_ramping_ringer">false</bool>
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
index eb81961..b84a9cd 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
@@ -40,6 +40,9 @@
public static final String[] SETTINGS_TO_BACKUP = {
Settings.Global.APPLY_RAMPING_RINGER,
Settings.Global.BUGREPORT_IN_POWER_MENU,
+ Settings.Global.CLOCKWORK_SYSUI_PACKAGE_NAME,
+ Settings.Global.CLOCKWORK_SYSUI_MAIN_ACTIVITY_NAME,
+ Settings.Global.CLOCKWORK_HOME_READY,
Settings.Global.STAY_ON_WHILE_PLUGGED_IN,
Settings.Global.APP_AUTO_RESTRICTION_ENABLED,
Settings.Global.AUTO_TIME,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 5220a04..c1ac9a9 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -113,6 +113,9 @@
VALIDATORS.put(
Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, PERCENTAGE_INTEGER_VALIDATOR);
VALIDATORS.put(Global.BLUETOOTH_ON, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.CLOCKWORK_SYSUI_MAIN_ACTIVITY_NAME, ANY_STRING_VALIDATOR);
+ VALIDATORS.put(Global.CLOCKWORK_SYSUI_PACKAGE_NAME, ANY_STRING_VALIDATOR);
+ VALIDATORS.put(Global.CLOCKWORK_HOME_READY, ANY_STRING_VALIDATOR);
VALIDATORS.put(Global.PRIVATE_DNS_MODE, ANY_STRING_VALIDATOR);
VALIDATORS.put(Global.PRIVATE_DNS_SPECIFIER, ANY_STRING_VALIDATOR);
VALIDATORS.put(Global.SOFT_AP_TIMEOUT_ENABLED, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 268603f..cdf274f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -2467,6 +2467,9 @@
loadBooleanSetting(stmt, Settings.Global.BLUETOOTH_ON,
R.bool.def_bluetooth_on);
+ loadIntegerSetting(stmt, Settings.Global.CELL_ON,
+ R.integer.def_cell_on);
+
// Enable or disable Cell Broadcast SMS
loadSetting(stmt, Settings.Global.CDMA_CELL_BROADCAST_SMS,
RILConstants.CDMA_CELL_BROADCAST_SMS_DISABLED);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index f04acd0..1ff5950 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -33,6 +33,7 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
+import android.provider.Settings.Global;
import android.providers.settings.SettingsOperationProto;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -47,6 +48,7 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import libcore.io.IoUtils;
@@ -806,7 +808,14 @@
final int settingCount = settings.size();
for (int i = 0; i < settingCount; i++) {
+
Setting setting = settings.valueAt(i);
+ if (setting.isTransient()) {
+ if (DEBUG_PERSISTENCE) {
+ Slog.i(LOG_TAG, "[SKIPPED PERSISTING]" + setting.getName());
+ }
+ continue;
+ }
if (writeSingleSetting(mVersion, serializer, setting.getId(), setting.getName(),
setting.getValue(), setting.getDefaultValue(), setting.getPackageName(),
@@ -1302,6 +1311,14 @@
/* resetToDefault */ true);
}
+ public boolean isTransient() {
+ switch (getTypeFromKey(getKey())) {
+ case SETTINGS_TYPE_GLOBAL:
+ return ArrayUtils.contains(Global.TRANSIENT_SETTINGS, getName());
+ }
+ return false;
+ }
+
public boolean update(String value, boolean setDefault, String packageName, String tag,
boolean forceNonSystemPackage, boolean overrideableByRestore) {
return update(value, setDefault, packageName, tag, forceNonSystemPackage,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 959b5ca..14f10ab 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -104,7 +104,6 @@
<uses-permission android:name="android.permission.PERSISTENT_ACTIVITY" />
<uses-permission android:name="android.permission.GET_PACKAGE_SIZE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
- <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
<uses-permission android:name="android.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
index 504e18a..56b940c 100644
--- a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
+++ b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
@@ -256,6 +256,14 @@
}
setupAlert();
+
+ ListView listView = mAlert.getListView();
+ if (listView != null) {
+ // List view needs to gain focus in order for RSB to work.
+ if (!listView.requestFocus()) {
+ Log.e(TAG, "Unable to gain focus! RSB may not work properly.");
+ }
+ }
}
@Override
public void onSaveInstanceState(Bundle outState) {
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index 835471d..1cf14f2 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -1,5 +1,7 @@
set noparent
+# Bug component: 78010
+
dsandler@android.com
aaliomer@google.com
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 0d18b8d..24bffa1 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -53,8 +53,9 @@
// Curious where your @Scenario tests will run?
//
- // @Ignore or @FlakyTest: nowhere
- // @Staging: in staged-postsubmit, but not postsubmit or presubmit
+ // @Ignore: nowhere
+ // @Staging or @FlakyTest: in staged-postsubmit, but not postsubmit or
+ // presubmit
// @Postsubmit: in postsubmit and staged-postsubmit, but not presubmit
// none of the above: in presubmit, postsubmit, and staged-postsubmit
//
@@ -98,9 +99,6 @@
},
{
"exclude-annotation": "org.junit.Ignore"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
}
diff --git a/packages/SystemUI/res/layout/global_actions_grid_lite.xml b/packages/SystemUI/res/layout/global_actions_grid_lite.xml
index 5588fd3..2430eec 100644
--- a/packages/SystemUI/res/layout/global_actions_grid_lite.xml
+++ b/packages/SystemUI/res/layout/global_actions_grid_lite.xml
@@ -13,12 +13,13 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<androidx.constraintlayout.widget.ConstraintLayout
+<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/global_actions_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:orientation="vertical"
android:gravity="center"
android:layout_gravity="center">
<com.android.systemui.globalactions.GlobalActionsLayoutLite
@@ -28,11 +29,8 @@
android:orientation="vertical"
android:clipChildren="false"
android:clipToPadding="false"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- android:layout_weight="1">
+ android:background="@drawable/global_actions_lite_background"
+ android:padding="@dimen/global_actions_lite_padding">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -40,8 +38,6 @@
android:gravity="center"
android:translationZ="@dimen/global_actions_translate"
android:orientation="horizontal"
- android:background="@drawable/global_actions_lite_background"
- android:padding="@dimen/global_actions_lite_padding"
android:layoutDirection="ltr">
<androidx.constraintlayout.helper.widget.Flow
android:id="@+id/list_flow"
@@ -57,4 +53,4 @@
app:flow_horizontalStyle="packed"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</com.android.systemui.globalactions.GlobalActionsLayoutLite>
-</androidx.constraintlayout.widget.ConstraintLayout>
+</LinearLayout>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index b4deaa0..f7eaea9 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -603,8 +603,6 @@
<!-- Whether wallet view is shown in landscape / seascape orientations -->
<bool name="global_actions_show_landscape_wallet_view">false</bool>
- <!-- Whether global actions should show an informational message about changes in S -->
- <bool name="global_actions_show_change_info">false</bool>
<!-- Package name of the preferred system app to perform eSOS action -->
<string name="config_preferredEmergencySosPackage" translatable="false"></string>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 76a4c5f..2e8d24b 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1114,7 +1114,6 @@
<dimen name="global_actions_button_padding">38dp</dimen>
<dimen name="global_actions_corner_radius">28dp</dimen>
<dimen name="global_actions_lite_padding">24dp</dimen>
- <dimen name="global_actions_info_margin">32dp</dimen>
<!-- The maximum offset in either direction that elements are moved horizontally to prevent
burn-in on AOD. -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 7f4e475..2f16ac2 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2976,7 +2976,4 @@
<!-- Content description for a chip in the status bar showing that the user is currently on a phone call. [CHAR LIMIT=NONE] -->
<string name="ongoing_phone_call_content_description">Ongoing phone call</string>
-
- <!-- URL for more information about changes in global actions -->
- <string name="global_actions_change_url" translatable="false"></string>
</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index 277b2e3..de9558e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -77,8 +77,17 @@
void onSplitScreenSecondaryBoundsChanged(in Rect bounds, in Rect insets) = 17;
/**
- * Sent IME status changes
+ * Sent when suggested rotation button could be shown
*/
- void onImeWindowStatusChanged(int displayId, IBinder token, int vis, int backDisposition,
- boolean showImeSwitcher) = 18;
+ void onRotationProposal(int rotation, boolean isValid) = 18;
+
+ /**
+ * Sent when disable flags change
+ */
+ void disable(int displayId, int state1, int state2, boolean animate) = 19;
+
+ /**
+ * Sent when behavior changes. See WindowInsetsController#@Behavior
+ */
+ void onSystemBarAttributesChanged(int displayId, int behavior) = 20;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
index 7dffc26..a624f061 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
@@ -16,9 +16,14 @@
package com.android.systemui.shared.recents.utilities;
+import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
+
import android.graphics.Color;
+import android.inputmethodservice.InputMethodService;
import android.os.Handler;
import android.os.Message;
+import android.view.Surface;
/* Common code */
public class Utilities {
@@ -31,6 +36,23 @@
h.sendMessageAtFrontOfQueue(msg);
}
+ public static boolean isRotationAnimationCCW(int from, int to) {
+ // All 180deg WM rotation animations are CCW, match that
+ if (from == Surface.ROTATION_0 && to == Surface.ROTATION_90) return false;
+ if (from == Surface.ROTATION_0 && to == Surface.ROTATION_180) return true; //180d so CCW
+ if (from == Surface.ROTATION_0 && to == Surface.ROTATION_270) return true;
+ if (from == Surface.ROTATION_90 && to == Surface.ROTATION_0) return true;
+ if (from == Surface.ROTATION_90 && to == Surface.ROTATION_180) return false;
+ if (from == Surface.ROTATION_90 && to == Surface.ROTATION_270) return true; //180d so CCW
+ if (from == Surface.ROTATION_180 && to == Surface.ROTATION_0) return true; //180d so CCW
+ if (from == Surface.ROTATION_180 && to == Surface.ROTATION_90) return true;
+ if (from == Surface.ROTATION_180 && to == Surface.ROTATION_270) return false;
+ if (from == Surface.ROTATION_270 && to == Surface.ROTATION_0) return false;
+ if (from == Surface.ROTATION_270 && to == Surface.ROTATION_90) return true; //180d so CCW
+ if (from == Surface.ROTATION_270 && to == Surface.ROTATION_180) return true;
+ return false; // Default
+ }
+
/** Calculates the constrast between two colors, using the algorithm provided by the WCAG v2. */
public static float computeContrastBetweenColors(int bg, int fg) {
float bgR = Color.red(bg) / 255f;
@@ -58,4 +80,34 @@
public static float clamp(float value, float min, float max) {
return Math.max(min, Math.min(max, value));
}
+
+ /**
+ * @return updated set of flags from InputMethodService based off {@param oldHints}
+ * Leaves original hints unmodified
+ */
+ public static int calculateBackDispositionHints(int oldHints, int backDisposition,
+ boolean imeShown, boolean showImeSwitcher) {
+ int hints = oldHints;
+ switch (backDisposition) {
+ case InputMethodService.BACK_DISPOSITION_DEFAULT:
+ case InputMethodService.BACK_DISPOSITION_WILL_NOT_DISMISS:
+ case InputMethodService.BACK_DISPOSITION_WILL_DISMISS:
+ if (imeShown) {
+ hints |= NAVIGATION_HINT_BACK_ALT;
+ } else {
+ hints &= ~NAVIGATION_HINT_BACK_ALT;
+ }
+ break;
+ case InputMethodService.BACK_DISPOSITION_ADJUST_NOTHING:
+ hints &= ~NAVIGATION_HINT_BACK_ALT;
+ break;
+ }
+ if (showImeSwitcher) {
+ hints |= NAVIGATION_HINT_IME_SHOWN;
+ } else {
+ hints &= ~NAVIGATION_HINT_IME_SHOWN;
+ }
+
+ return hints;
+ }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/ViewRippler.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/ViewRippler.java
new file mode 100644
index 0000000..5581a1c
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/ViewRippler.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.recents.utilities;
+
+import android.view.View;
+
+/**
+ * Shows view ripples by toggling the provided Views "pressed" state.
+ * Ripples 4 times.
+ */
+public class ViewRippler {
+ private static final int RIPPLE_OFFSET_MS = 50;
+ private static final int RIPPLE_INTERVAL_MS = 2000;
+ private View mRoot;
+
+ public void start(View root) {
+ stop(); // Stop any pending ripple animations
+
+ mRoot = root;
+
+ // Schedule pending ripples, offset the 1st to avoid problems with visibility change
+ mRoot.postOnAnimationDelayed(mRipple, RIPPLE_OFFSET_MS);
+ mRoot.postOnAnimationDelayed(mRipple, RIPPLE_INTERVAL_MS);
+ mRoot.postOnAnimationDelayed(mRipple, 2 * RIPPLE_INTERVAL_MS);
+ mRoot.postOnAnimationDelayed(mRipple, 3 * RIPPLE_INTERVAL_MS);
+ mRoot.postOnAnimationDelayed(mRipple, 4 * RIPPLE_INTERVAL_MS);
+ }
+
+ public void stop() {
+ if (mRoot != null) mRoot.removeCallbacks(mRipple);
+ }
+
+ private final Runnable mRipple = new Runnable() {
+ @Override
+ public void run() { // Cause the ripple to fire via false presses
+ if (!mRoot.isAttachedToWindow()) return;
+ mRoot.setPressed(true /* pressed */);
+ mRoot.setPressed(false /* pressed */);
+ }
+ };
+}
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index c468e41..153708a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -112,6 +112,8 @@
public static final int SYSUI_STATE_IME_SHOWING = 1 << 18;
// The window magnification is overlapped with system gesture insets at the bottom.
public static final int SYSUI_STATE_MAGNIFICATION_OVERLAP = 1 << 19;
+ // ImeSwitcher is showing
+ public static final int SYSUI_STATE_IME_SWITCHER_SHOWING = 1 << 20;
@Retention(RetentionPolicy.SOURCE)
@IntDef({SYSUI_STATE_SCREEN_PINNING,
@@ -133,7 +135,8 @@
SYSUI_STATE_ONE_HANDED_ACTIVE,
SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
SYSUI_STATE_IME_SHOWING,
- SYSUI_STATE_MAGNIFICATION_OVERLAP
+ SYSUI_STATE_MAGNIFICATION_OVERLAP,
+ SYSUI_STATE_IME_SWITCHER_SHOWING
})
public @interface SystemUiStateFlags {}
@@ -162,6 +165,7 @@
? "allow_gesture" : "");
str.add((flags & SYSUI_STATE_IME_SHOWING) != 0 ? "ime_visible" : "");
str.add((flags & SYSUI_STATE_MAGNIFICATION_OVERLAP) != 0 ? "magnification_overlap" : "");
+ str.add((flags & SYSUI_STATE_IME_SWITCHER_SHOWING) != 0 ? "ime_switcher_showing" : "");
return str.toString();
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
index ee55bf0..fdd1abe 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
@@ -260,7 +260,7 @@
t.remove(leashMap.valueAt(i));
}
t.apply();
- finishCallback.onTransitionFinished(null /* wct */);
+ finishCallback.onTransitionFinished(null /* wct */, null /* sct */);
} catch (RemoteException e) {
Log.e("ActivityOptionsCompat", "Failed to call app controlled animation"
+ " finished callback", e);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
index 653d730..11af6ef 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
@@ -25,6 +25,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
+import android.app.ActivityTaskManager;
import android.graphics.Rect;
import android.os.IBinder;
import android.os.Parcelable;
@@ -73,7 +74,7 @@
IRemoteTransitionFinishedCallback finishedCallback) {
final Runnable finishAdapter = () -> {
try {
- finishedCallback.onTransitionFinished(null /* wct */);
+ finishedCallback.onTransitionFinished(null /* wct */, null /* sct */);
} catch (RemoteException e) {
Log.e(TAG, "Failed to call transition finished callback", e);
}
@@ -87,7 +88,7 @@
IRemoteTransitionFinishedCallback finishedCallback) {
final Runnable finishAdapter = () -> {
try {
- finishedCallback.onTransitionFinished(null /* wct */);
+ finishedCallback.onTransitionFinished(null /* wct */, null /* sct */);
} catch (RemoteException e) {
Log.e(TAG, "Failed to call transition finished callback", e);
}
@@ -119,6 +120,7 @@
// This transition is for opening recents, so recents is on-top. We want to draw
// the current going-away task on top of recents, though, so move it to front
WindowContainerToken pausingTask = null;
+ SurfaceControl pausingLeash = null;
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
if (change.getMode() == TRANSIT_CLOSE || change.getMode() == TRANSIT_TO_BACK) {
@@ -135,7 +137,7 @@
}
t.apply();
mRecentsSession.setup(controller, info, finishedCallback, pausingTask,
- leashMap);
+ leashMap, mToken);
recents.onAnimationStart(mRecentsSession, apps, wallpapers, new Rect(0, 0, 0, 0),
new Rect());
}
@@ -147,7 +149,7 @@
if (!mergeTarget.equals(mToken)) return;
if (!mRecentsSession.merge(info, t, recents)) return;
try {
- finishedCallback.onTransitionFinished(null /* wct */);
+ finishedCallback.onTransitionFinished(null /* wct */, null /* sct */);
} catch (RemoteException e) {
Log.e(TAG, "Error merging transition.", e);
}
@@ -178,10 +180,11 @@
private TransitionInfo mInfo = null;
private SurfaceControl mOpeningLeash = null;
private ArrayMap<SurfaceControl, SurfaceControl> mLeashMap = null;
+ private IBinder mTransition = null;
void setup(RecentsAnimationControllerCompat wrapped, TransitionInfo info,
IRemoteTransitionFinishedCallback finishCB, WindowContainerToken pausingTask,
- ArrayMap<SurfaceControl, SurfaceControl> leashMap) {
+ ArrayMap<SurfaceControl, SurfaceControl> leashMap, IBinder transition) {
if (mInfo != null) {
throw new IllegalStateException("Trying to run a new recents animation while"
+ " recents is already active.");
@@ -191,6 +194,7 @@
mFinishCB = finishCB;
mPausingTask = pausingTask;
mLeashMap = leashMap;
+ mTransition = transition;
}
@SuppressLint("NewApi")
@@ -263,10 +267,13 @@
try {
if (!toHome && mPausingTask != null && mOpeningLeash == null) {
// The gesture went back to opening the app rather than continuing with
- // recents, so end the transition by moving the app back to the top.
+ // recents, so end the transition by moving the app back to the top (and also
+ // re-showing it's task).
final WindowContainerTransaction wct = new WindowContainerTransaction();
wct.reorder(mPausingTask, true /* onTop */);
- mFinishCB.onTransitionFinished(wct);
+ final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ t.show(mInfo.getChange(mPausingTask).getLeash());
+ mFinishCB.onTransitionFinished(wct, t);
} else {
if (mOpeningLeash != null) {
// TODO: the launcher animation should handle this
@@ -275,7 +282,7 @@
t.setAlpha(mOpeningLeash, 1.f);
t.apply();
}
- mFinishCB.onTransitionFinished(null /* wct */);
+ mFinishCB.onTransitionFinished(null /* wct */, null /* sct */);
}
} catch (RemoteException e) {
Log.e("RemoteTransitionCompat", "Failed to call animation finish callback", e);
@@ -298,6 +305,7 @@
mInfo = null;
mOpeningLeash = null;
mLeashMap = null;
+ mTransition = null;
}
@Override public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
@@ -318,6 +326,17 @@
@Override public boolean removeTask(int taskId) {
return mWrapped != null ? mWrapped.removeTask(taskId) : false;
}
+
+ /**
+ * @see IRecentsAnimationController#detachNavigationBarFromApp
+ */
+ @Override public void detachNavigationBarFromApp(boolean moveHomeToTop) {
+ try {
+ ActivityTaskManager.getService().detachNavigationBarFromApp(mTransition);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to detach the navigation bar from app", e);
+ }
+ }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.aidl
index 2d01d6a..e49d9f9 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.aidl
@@ -16,6 +16,4 @@
package com.android.systemui.shared.system.smartspace;
-import com.android.systemui.shared.system.smartspace.SmartspaceState;
-
-parcelable SmartspaceState;
\ No newline at end of file
+parcelable SmartspaceState;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index ef052c4..0bd9ca4 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -14,6 +14,9 @@
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
+import androidx.annotation.IntDef;
+import androidx.annotation.VisibleForTesting;
+
import com.android.internal.colorextraction.ColorExtractor;
import com.android.keyguard.dagger.KeyguardStatusViewScope;
import com.android.systemui.R;
@@ -22,6 +25,8 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.TimeZone;
@@ -37,6 +42,13 @@
private static final long CLOCK_IN_MILLIS = 200;
private static final long SMARTSPACE_MOVE_MILLIS = 350;
+ @IntDef({LARGE, SMALL})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ClockSize { }
+
+ public static final int LARGE = 0;
+ public static final int SMALL = 1;
+
/**
* Optional/alternative clock injected via plugin.
*/
@@ -64,13 +76,13 @@
private float mDarkAmount;
/**
- * Boolean value indicating if notifications are visible on lock screen. Use null to signify
- * it is uninitialized.
+ * Indicates which clock is currently displayed - should be one of {@link ClockSize}.
+ * Use null to signify it is uninitialized.
*/
- private Boolean mHasVisibleNotifications = null;
+ @ClockSize private Integer mDisplayedClockSize = null;
- private AnimatorSet mClockInAnim = null;
- private AnimatorSet mClockOutAnim = null;
+ @VisibleForTesting AnimatorSet mClockInAnim = null;
+ @VisibleForTesting AnimatorSet mClockOutAnim = null;
private ObjectAnimator mSmartspaceAnim = null;
/**
@@ -260,19 +272,17 @@
}
/**
- * Based upon whether notifications are showing or not, display/hide the large clock and
- * the smaller version.
+ * Display the desired clock and hide the other one
+ *
+ * @return true if desired clock appeared and false if it was already visible
*/
- boolean willSwitchToLargeClock(boolean hasVisibleNotifications) {
- if (mHasVisibleNotifications != null
- && hasVisibleNotifications == mHasVisibleNotifications) {
+ boolean switchToClock(@ClockSize int clockSize) {
+ if (mDisplayedClockSize != null && clockSize == mDisplayedClockSize) {
return false;
}
- boolean useLargeClock = !hasVisibleNotifications;
- animateClockChange(useLargeClock);
-
- mHasVisibleNotifications = hasVisibleNotifications;
- return useLargeClock;
+ animateClockChange(clockSize == LARGE);
+ mDisplayedClockSize = clockSize;
+ return true;
}
public Paint getPaint() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index ef6212d..a928270 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -19,6 +19,8 @@
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static com.android.keyguard.KeyguardClockSwitch.LARGE;
+
import android.app.WallpaperManager;
import android.content.res.Resources;
import android.text.TextUtils;
@@ -234,10 +236,12 @@
}
/**
- * Set whether or not the lock screen is showing notifications.
+ * Set which clock should be displayed on the keyguard. The other one will be automatically
+ * hidden.
*/
- public void setHasVisibleNotifications(boolean hasVisibleNotifications) {
- if (mView.willSwitchToLargeClock(hasVisibleNotifications)) {
+ public void displayClock(@KeyguardClockSwitch.ClockSize int clockSize) {
+ boolean appeared = mView.switchToClock(clockSize);
+ if (appeared && clockSize == LARGE) {
mLargeClockViewController.animateAppear();
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index ca4d73b..840e8c8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -42,7 +42,6 @@
import android.view.WindowManager;
import android.widget.FrameLayout;
-import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.SpringAnimation;
@@ -104,7 +103,6 @@
private boolean mIsSecurityViewLeftAligned = true;
private boolean mOneHandedMode = false;
- private SecurityMode mSecurityMode = SecurityMode.Invalid;
private ViewPropertyAnimator mRunningOneHandedAnimator;
private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
@@ -248,66 +246,47 @@
}
void onResume(SecurityMode securityMode, boolean faceAuthEnabled) {
- mSecurityMode = securityMode;
mSecurityViewFlipper.setWindowInsetsAnimationCallback(mWindowInsetsAnimationCallback);
updateBiometricRetry(securityMode, faceAuthEnabled);
-
- updateLayoutForSecurityMode(securityMode);
}
- void updateLayoutForSecurityMode(SecurityMode securityMode) {
- mSecurityMode = securityMode;
- mOneHandedMode = canUseOneHandedBouncer();
-
- if (mOneHandedMode) {
- mIsSecurityViewLeftAligned = isOneHandedKeyguardLeftAligned(mContext);
- }
-
+ /**
+ * Sets whether this security container is in one handed mode. If so, it will measure its
+ * child SecurityViewFlipper in one half of the screen, and move it when tapping on the opposite
+ * side of the screen.
+ */
+ public void setOneHandedMode(boolean oneHandedMode) {
+ mOneHandedMode = oneHandedMode;
updateSecurityViewGravity();
updateSecurityViewLocation(false);
}
- /** Update keyguard position based on a tapped X coordinate. */
- public void updateKeyguardPosition(float x) {
- if (mOneHandedMode) {
- moveBouncerForXCoordinate(x, /* animate= */false);
- }
+ /** Returns whether this security container is in one-handed mode. */
+ public boolean isOneHandedMode() {
+ return mOneHandedMode;
}
- /** Return whether the one-handed keyguard should be enabled. */
- private boolean canUseOneHandedBouncer() {
- // Is it enabled?
- if (!getResources().getBoolean(
- com.android.internal.R.bool.config_enableDynamicKeyguardPositioning)) {
- return false;
- }
-
- if (!KeyguardSecurityModel.isSecurityViewOneHanded(mSecurityMode)) {
- return false;
- }
-
- return getResources().getBoolean(R.bool.can_use_one_handed_bouncer);
+ /**
+ * When in one-handed mode, sets if the inner SecurityViewFlipper should be aligned to the
+ * left-hand side of the screen or not, and whether to animate when moving between the two.
+ */
+ public void setOneHandedModeLeftAligned(boolean leftAligned, boolean animate) {
+ mIsSecurityViewLeftAligned = leftAligned;
+ updateSecurityViewLocation(animate);
}
- /** Read whether the one-handed keyguard should be on the left/right from settings. */
- private boolean isOneHandedKeyguardLeftAligned(Context context) {
- try {
- return Settings.Global.getInt(context.getContentResolver(),
- Settings.Global.ONE_HANDED_KEYGUARD_SIDE)
- == Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT;
- } catch (Settings.SettingNotFoundException ex) {
- return true;
- }
+ /** Returns whether the inner SecurityViewFlipper is left-aligned when in one-handed mode. */
+ public boolean isOneHandedModeLeftAligned() {
+ return mIsSecurityViewLeftAligned;
}
private void updateSecurityViewGravity() {
- View securityView = findKeyguardSecurityView();
-
- if (securityView == null) {
+ if (mSecurityViewFlipper == null) {
return;
}
- FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) securityView.getLayoutParams();
+ FrameLayout.LayoutParams lp =
+ (FrameLayout.LayoutParams) mSecurityViewFlipper.getLayoutParams();
if (mOneHandedMode) {
lp.gravity = Gravity.LEFT | Gravity.BOTTOM;
@@ -315,7 +294,7 @@
lp.gravity = Gravity.CENTER_HORIZONTAL;
}
- securityView.setLayoutParams(lp);
+ mSecurityViewFlipper.setLayoutParams(lp);
}
/**
@@ -324,14 +303,12 @@
* by the security view .
*/
private void updateSecurityViewLocation(boolean animate) {
- View securityView = findKeyguardSecurityView();
-
- if (securityView == null) {
+ if (mSecurityViewFlipper == null) {
return;
}
if (!mOneHandedMode) {
- securityView.setTranslationX(0);
+ mSecurityViewFlipper.setTranslationX(0);
return;
}
@@ -343,7 +320,8 @@
int targetTranslation = mIsSecurityViewLeftAligned ? 0 : (int) (getMeasuredWidth() / 2f);
if (animate) {
- mRunningOneHandedAnimator = securityView.animate().translationX(targetTranslation);
+ mRunningOneHandedAnimator =
+ mSecurityViewFlipper.animate().translationX(targetTranslation);
mRunningOneHandedAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
mRunningOneHandedAnimator.setListener(new AnimatorListenerAdapter() {
@Override
@@ -355,27 +333,10 @@
mRunningOneHandedAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
mRunningOneHandedAnimator.start();
} else {
- securityView.setTranslationX(targetTranslation);
+ mSecurityViewFlipper.setTranslationX(targetTranslation);
}
}
- @Nullable
- private KeyguardSecurityViewFlipper findKeyguardSecurityView() {
- for (int i = 0; i < getChildCount(); i++) {
- View child = getChildAt(i);
-
- if (isKeyguardSecurityView(child)) {
- return (KeyguardSecurityViewFlipper) child;
- }
- }
-
- return null;
- }
-
- private boolean isKeyguardSecurityView(View view) {
- return view instanceof KeyguardSecurityViewFlipper;
- }
-
public void onPause() {
if (mAlertDialog != null) {
mAlertDialog.dismiss();
@@ -635,7 +596,7 @@
for (int i = 0; i < getChildCount(); i++) {
final View view = getChildAt(i);
if (view.getVisibility() != GONE) {
- if (mOneHandedMode && isKeyguardSecurityView(view)) {
+ if (mOneHandedMode && view == mSecurityViewFlipper) {
measureChildWithMargins(view, halfWidthMeasureSpec, 0,
heightMeasureSpec, 0);
} else {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 4827cab..6826e7b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -32,6 +32,7 @@
import android.content.res.Configuration;
import android.metrics.LogMaker;
import android.os.UserHandle;
+import android.provider.Settings;
import android.util.Log;
import android.util.Slog;
import android.view.MotionEvent;
@@ -49,6 +50,8 @@
import com.android.keyguard.dagger.KeyguardBouncerScope;
import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.Gefingerpoken;
+import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -74,12 +77,14 @@
private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController;
private final SecurityCallback mSecurityCallback;
private final ConfigurationController mConfigurationController;
+ private final FalsingCollector mFalsingCollector;
private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED;
private SecurityMode mCurrentSecurityMode = SecurityMode.Invalid;
- private final Gefingerpoken mGlobalTouchListener = new Gefingerpoken() {
+ @VisibleForTesting
+ final Gefingerpoken mGlobalTouchListener = new Gefingerpoken() {
private MotionEvent mTouchDown;
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
@@ -91,6 +96,17 @@
// Do just a bit of our own falsing. People should only be tapping on the input, not
// swiping.
if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ // If we're in one handed mode, the user can tap on the opposite side of the screen
+ // to move the bouncer across. In that case, inhibit the falsing (otherwise the taps
+ // to move the bouncer to each screen side can end up closing it instead).
+ if (mView.isOneHandedMode()) {
+ if ((mView.isOneHandedModeLeftAligned() && ev.getX() > mView.getWidth() / 2f)
+ || (!mView.isOneHandedModeLeftAligned()
+ && ev.getX() <= mView.getWidth() / 2f)) {
+ mFalsingCollector.avoidGesture();
+ }
+ }
+
if (mTouchDown != null) {
mTouchDown.recycle();
mTouchDown = null;
@@ -202,7 +218,8 @@
KeyguardStateController keyguardStateController,
SecurityCallback securityCallback,
KeyguardSecurityViewFlipperController securityViewFlipperController,
- ConfigurationController configurationController) {
+ ConfigurationController configurationController,
+ FalsingCollector falsingCollector) {
super(view);
mLockPatternUtils = lockPatternUtils;
mUpdateMonitor = keyguardUpdateMonitor;
@@ -216,6 +233,7 @@
mKeyguardSecurityCallback);
mConfigurationController = configurationController;
mLastOrientation = getResources().getConfiguration().orientation;
+ mFalsingCollector = falsingCollector;
}
@Override
@@ -440,13 +458,49 @@
if (newView != null) {
newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
mSecurityViewFlipperController.show(newView);
- mView.updateLayoutForSecurityMode(securityMode);
+ configureOneHandedMode();
}
mSecurityCallback.onSecurityModeChanged(
securityMode, newView != null && newView.needsInput());
}
+ /** Read whether the one-handed keyguard should be on the left/right from settings. */
+ private boolean isOneHandedKeyguardLeftAligned() {
+ try {
+ return Settings.Global.getInt(mView.getContext().getContentResolver(),
+ Settings.Global.ONE_HANDED_KEYGUARD_SIDE)
+ == Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT;
+ } catch (Settings.SettingNotFoundException ex) {
+ return true;
+ }
+ }
+
+ private boolean canUseOneHandedBouncer() {
+ // Is it enabled?
+ if (!getResources().getBoolean(
+ com.android.internal.R.bool.config_enableDynamicKeyguardPositioning)) {
+ return false;
+ }
+
+ if (!KeyguardSecurityModel.isSecurityViewOneHanded(mCurrentSecurityMode)) {
+ return false;
+ }
+
+ return getResources().getBoolean(R.bool.can_use_one_handed_bouncer);
+ }
+
+ private void configureOneHandedMode() {
+ boolean oneHandedMode = canUseOneHandedBouncer();
+
+ mView.setOneHandedMode(oneHandedMode);
+
+ if (oneHandedMode) {
+ mView.setOneHandedModeLeftAligned(
+ isOneHandedKeyguardLeftAligned(), /* animate= */false);
+ }
+ }
+
public void reportFailedUnlockAttempt(int userId, int timeoutMs) {
// +1 for this time
final int failedAttempts = mLockPatternUtils.getCurrentFailedPasswordAttempts(userId) + 1;
@@ -511,13 +565,15 @@
int newOrientation = getResources().getConfiguration().orientation;
if (newOrientation != mLastOrientation) {
mLastOrientation = newOrientation;
- mView.updateLayoutForSecurityMode(mCurrentSecurityMode);
+ configureOneHandedMode();
}
}
/** Update keyguard position based on a tapped X coordinate. */
public void updateKeyguardPosition(float x) {
- mView.updateKeyguardPosition(x);
+ if (mView.isOneHandedMode()) {
+ mView.setOneHandedModeLeftAligned(x <= mView.getWidth() / 2f, false);
+ }
}
static class Factory {
@@ -533,6 +589,7 @@
private final KeyguardStateController mKeyguardStateController;
private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController;
private final ConfigurationController mConfigurationController;
+ private final FalsingCollector mFalsingCollector;
@Inject
Factory(KeyguardSecurityContainer view,
@@ -545,7 +602,8 @@
UiEventLogger uiEventLogger,
KeyguardStateController keyguardStateController,
KeyguardSecurityViewFlipperController securityViewFlipperController,
- ConfigurationController configurationController) {
+ ConfigurationController configurationController,
+ FalsingCollector falsingCollector) {
mView = view;
mAdminSecondaryLockScreenControllerFactory = adminSecondaryLockScreenControllerFactory;
mLockPatternUtils = lockPatternUtils;
@@ -556,6 +614,7 @@
mKeyguardStateController = keyguardStateController;
mSecurityViewFlipperController = securityViewFlipperController;
mConfigurationController = configurationController;
+ mFalsingCollector = falsingCollector;
}
public KeyguardSecurityContainerController create(
@@ -564,7 +623,7 @@
mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
mKeyguardStateController, securityCallback, mSecurityViewFlipperController,
- mConfigurationController);
+ mConfigurationController, mFalsingCollector);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 123c0e6..2f485a0 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -19,6 +19,7 @@
import android.graphics.Rect;
import android.util.Slog;
+import com.android.keyguard.KeyguardClockSwitch.ClockSize;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController;
import com.android.systemui.statusbar.notification.AnimatableProperty;
@@ -126,10 +127,11 @@
}
/**
- * Set whether or not the lock screen is showing notifications.
+ * Set which clock should be displayed on the keyguard. The other one will be automatically
+ * hidden.
*/
- public void setHasVisibleNotifications(boolean hasVisibleNotifications) {
- mKeyguardClockSwitchController.setHasVisibleNotifications(hasVisibleNotifications);
+ public void displayClock(@ClockSize int clockSize) {
+ mKeyguardClockSwitchController.displayClock(clockSize);
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 3d51f23..783d814 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -2738,6 +2738,20 @@
updateBiometricListeningState();
}
+ /** Notifies that the occluded state changed. */
+ public void onKeyguardOccludedChanged(boolean occluded) {
+ Assert.isMainThread();
+ if (DEBUG) {
+ Log.d(TAG, "onKeyguardOccludedChanged(" + occluded + ")");
+ }
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onKeyguardOccludedChanged(occluded);
+ }
+ }
+ }
+
/**
* Handle {@link #MSG_KEYGUARD_RESET}
*/
@@ -2920,6 +2934,7 @@
callback.onPhoneStateChanged(mPhoneState);
callback.onRefreshCarrierInfo();
callback.onClockVisibilityChanged();
+ callback.onKeyguardOccludedChanged(mKeyguardOccluded);
callback.onKeyguardVisibilityChangedRaw(mKeyguardIsVisible);
callback.onTelephonyCapable(mTelephonyCapable);
callback.onLockScreenModeChanged(mLockScreenMode);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index e561a5a..20a0e31 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -88,6 +88,12 @@
*/
public void onKeyguardVisibilityChanged(boolean showing) { }
+ /**
+ * Called when the keyguard occluded state changes.
+ * @param occluded Indicates if the keyguard is now occluded.
+ */
+ public void onKeyguardOccludedChanged(boolean occluded) { }
+
public void onKeyguardVisibilityChangedRaw(boolean showing) {
final long now = SystemClock.elapsedRealtime();
if (showing == mShowing
diff --git a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
index 45a0ea1..95c6825 100644
--- a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
@@ -17,9 +17,8 @@
package com.android.systemui;
import android.app.ActivityManager;
-import android.app.Dialog;
+import android.app.AlertDialog;
import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -28,14 +27,16 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
-import android.provider.Settings;
import android.util.Log;
import android.view.WindowManagerGlobal;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.qs.QSUserSwitcherEvent;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.util.settings.SecureSettings;
/**
* Manages notification when a guest session is resumed.
@@ -44,13 +45,20 @@
private static final String TAG = "GuestResumeSessionReceiver";
- private static final String SETTING_GUEST_HAS_LOGGED_IN = "systemui.guest_has_logged_in";
+ @VisibleForTesting
+ public static final String SETTING_GUEST_HAS_LOGGED_IN = "systemui.guest_has_logged_in";
- private Dialog mNewSessionDialog;
+ @VisibleForTesting
+ public AlertDialog mNewSessionDialog;
+ private final UserTracker mUserTracker;
private final UiEventLogger mUiEventLogger;
+ private final SecureSettings mSecureSettings;
- public GuestResumeSessionReceiver(UiEventLogger uiEventLogger) {
+ public GuestResumeSessionReceiver(UserTracker userTracker, UiEventLogger uiEventLogger,
+ SecureSettings secureSettings) {
+ mUserTracker = userTracker;
mUiEventLogger = uiEventLogger;
+ mSecureSettings = secureSettings;
}
/**
@@ -76,25 +84,19 @@
return;
}
- UserInfo currentUser;
- try {
- currentUser = ActivityManager.getService().getCurrentUser();
- } catch (RemoteException e) {
- return;
- }
+ UserInfo currentUser = mUserTracker.getUserInfo();
if (!currentUser.isGuest()) {
return;
}
- ContentResolver cr = context.getContentResolver();
- int notFirstLogin = Settings.System.getIntForUser(
- cr, SETTING_GUEST_HAS_LOGGED_IN, 0, userId);
+ int notFirstLogin = mSecureSettings.getIntForUser(
+ SETTING_GUEST_HAS_LOGGED_IN, 0, userId);
if (notFirstLogin != 0) {
- mNewSessionDialog = new ResetSessionDialog(context, mUiEventLogger, userId);
+ mNewSessionDialog = new ResetSessionDialog(context, mUserTracker, mUiEventLogger,
+ userId);
mNewSessionDialog.show();
} else {
- Settings.System.putIntForUser(
- cr, SETTING_GUEST_HAS_LOGGED_IN, 1, userId);
+ mSecureSettings.putIntForUser(SETTING_GUEST_HAS_LOGGED_IN, 1, userId);
}
}
}
@@ -104,15 +106,9 @@
*
* The guest must be the current user and its id must be {@param userId}.
*/
- private static void wipeGuestSession(Context context, int userId) {
+ private static void wipeGuestSession(Context context, UserTracker userTracker, int userId) {
UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
- UserInfo currentUser;
- try {
- currentUser = ActivityManager.getService().getCurrentUser();
- } catch (RemoteException e) {
- Log.e(TAG, "Couldn't wipe session because ActivityManager is dead");
- return;
- }
+ UserInfo currentUser = userTracker.getUserInfo();
if (currentUser.id != userId) {
Log.w(TAG, "User requesting to start a new session (" + userId + ")"
+ " is not current user (" + currentUser.id + ")");
@@ -154,16 +150,24 @@
}
}
- private static class ResetSessionDialog extends SystemUIDialog implements
+ /**
+ * Dialog shown when user when asking for confirmation before deleting guest user.
+ */
+ @VisibleForTesting
+ public static class ResetSessionDialog extends SystemUIDialog implements
DialogInterface.OnClickListener {
- private static final int BUTTON_WIPE = BUTTON_NEGATIVE;
- private static final int BUTTON_DONTWIPE = BUTTON_POSITIVE;
+ @VisibleForTesting
+ public static final int BUTTON_WIPE = BUTTON_NEGATIVE;
+ @VisibleForTesting
+ public static final int BUTTON_DONTWIPE = BUTTON_POSITIVE;
+ private final UserTracker mUserTracker;
private final UiEventLogger mUiEventLogger;
private final int mUserId;
- ResetSessionDialog(Context context, UiEventLogger uiEventLogger, int userId) {
+ ResetSessionDialog(Context context, UserTracker userTracker, UiEventLogger uiEventLogger,
+ int userId) {
super(context);
setTitle(context.getString(R.string.guest_wipe_session_title));
@@ -175,6 +179,7 @@
setButton(BUTTON_DONTWIPE,
context.getString(R.string.guest_wipe_session_dontwipe), this);
+ mUserTracker = userTracker;
mUiEventLogger = uiEventLogger;
mUserId = userId;
}
@@ -183,7 +188,7 @@
public void onClick(DialogInterface dialog, int which) {
if (which == BUTTON_WIPE) {
mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_WIPE);
- wipeGuestSession(getContext(), mUserId);
+ wipeGuestSession(getContext(), mUserTracker, mUserId);
dismiss();
} else if (which == BUTTON_DONTWIPE) {
mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_CONTINUE);
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 567d0cb..557bca8 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -120,7 +120,7 @@
private val onSeedingComplete = Consumer<Boolean> {
accepted ->
if (accepted) {
- selectedStructure = controlsController.get().getFavorites().maxBy {
+ selectedStructure = controlsController.get().getFavorites().maxByOrNull {
it.controls.size
} ?: EMPTY_STRUCTURE
updatePreferences(selectedStructure)
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index 746621d..9a121ba 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -60,6 +60,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.model.SysUiState;
+import com.android.systemui.navigationbar.NavigationBarA11yHelper;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBarOverlayController;
import com.android.systemui.navigationbar.NavigationModeController;
@@ -231,7 +232,8 @@
@Main Handler mainHandler,
UiEventLogger uiEventLogger,
NavigationBarOverlayController navBarOverlayController,
- ConfigurationController configurationController) {
+ ConfigurationController configurationController,
+ NavigationBarA11yHelper navigationBarA11yHelper) {
return new NavigationBarController(context,
windowManager,
assistManagerLazy,
@@ -256,7 +258,8 @@
mainHandler,
uiEventLogger,
navBarOverlayController,
- configurationController);
+ configurationController,
+ navigationBarA11yHelper);
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index dfd85fe..3873c25 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -151,7 +151,6 @@
iWindowManager,
backgroundExecutor,
uiEventLogger,
- null,
ringerModeTracker, sysUiState, handler);
mLockPatternUtils = lockPatternUtils;
@@ -299,7 +298,7 @@
super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions,
adapter, overflowAdapter, depthController, sysuiColorExtractor,
statusBarService, notificationShadeWindowController, sysuiState,
- onRotateCallback, keyguardShowing, powerAdapter, uiEventLogger, null);
+ onRotateCallback, keyguardShowing, powerAdapter, uiEventLogger);
mWalletFactory = walletFactory;
// Update window attributes
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index 8e15283..f9bb35fc 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -188,7 +188,6 @@
private final UiEventLogger mUiEventLogger;
private final NotificationShadeDepthController mDepthController;
private final SysUiState mSysUiState;
- private final GlobalActionsInfoProvider mInfoProvider;
// Used for RingerModeTracker
private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
@@ -321,7 +320,6 @@
IWindowManager iWindowManager,
@Background Executor backgroundExecutor,
UiEventLogger uiEventLogger,
- GlobalActionsInfoProvider infoProvider,
RingerModeTracker ringerModeTracker, SysUiState sysUiState, @Main Handler handler) {
mContext = context;
mWindowManagerFuncs = windowManagerFuncs;
@@ -341,7 +339,6 @@
mTelecomManager = telecomManager;
mMetricsLogger = metricsLogger;
mUiEventLogger = uiEventLogger;
- mInfoProvider = infoProvider;
mDepthController = depthController;
mSysuiColorExtractor = colorExtractor;
mStatusBarService = statusBarService;
@@ -624,8 +621,7 @@
mAdapter, mOverflowAdapter,
mDepthController, mSysuiColorExtractor,
mStatusBarService, mNotificationShadeWindowController,
- mSysUiState, this::onRotate, mKeyguardShowing, mPowerAdapter, mUiEventLogger,
- mInfoProvider);
+ mSysUiState, this::onRotate, mKeyguardShowing, mPowerAdapter, mUiEventLogger);
dialog.setOnDismissListener(this);
dialog.setOnShowListener(this);
@@ -2099,7 +2095,6 @@
private Dialog mPowerOptionsDialog;
protected final Runnable mOnRotateCallback;
private UiEventLogger mUiEventLogger;
- private GlobalActionsInfoProvider mInfoProvider;
protected ViewGroup mContainer;
@@ -2109,8 +2104,7 @@
SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
NotificationShadeWindowController notificationShadeWindowController,
SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing,
- MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger,
- @Nullable GlobalActionsInfoProvider infoProvider) {
+ MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger) {
super(context, themeRes);
mContext = context;
mAdapter = adapter;
@@ -2124,7 +2118,6 @@
mOnRotateCallback = onRotateCallback;
mKeyguardShowing = keyguardShowing;
mUiEventLogger = uiEventLogger;
- mInfoProvider = infoProvider;
// Window initialization
Window window = getWindow();
@@ -2222,10 +2215,6 @@
mBackgroundDrawable = new ScrimDrawable();
mScrimAlpha = 1.0f;
}
-
- if (mInfoProvider != null && mInfoProvider.shouldShowMessage()) {
- mInfoProvider.addPanel(mContext, mContainer, mAdapter.getCount(), () -> dismiss());
- }
}
protected void fixNavBarClipping() {
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt
deleted file mode 100644
index 39008ee..0000000
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.globalactions
-
-import android.app.PendingIntent
-import android.content.Context
-import android.content.Intent
-import android.content.res.Configuration
-import android.net.Uri
-import android.service.quickaccesswallet.QuickAccessWalletClient
-import android.util.Log
-import android.view.LayoutInflater
-import android.view.ViewGroup
-import android.widget.ImageView
-import com.android.systemui.R
-import com.android.systemui.controls.controller.ControlsController
-import com.android.systemui.plugins.ActivityStarter
-import javax.inject.Inject
-
-private const val TAG = "GlobalActionsInfo"
-
-/** Maximum number of times to show change info message */
-private const val MAX_VIEW_COUNT = 3
-
-/** Maximum number of buttons allowed in landscape before this panel does not fit */
-private const val MAX_BUTTONS_LANDSCAPE = 4
-
-private const val PREFERENCE = "global_actions_info_prefs"
-private const val KEY_VIEW_COUNT = "view_count"
-
-class GlobalActionsInfoProvider @Inject constructor(
- private val context: Context,
- private val walletClient: QuickAccessWalletClient,
- private val controlsController: ControlsController,
- private val activityStarter: ActivityStarter
-) {
-
- private var pendingIntent: PendingIntent
-
- init {
- val url = context.resources.getString(R.string.global_actions_change_url)
- val intent = Intent(Intent.ACTION_VIEW).apply {
- setData(Uri.parse(url))
- addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- }
- pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
- }
-
- fun addPanel(context: Context, parent: ViewGroup, nActions: Int, dismissParent: Runnable) {
- // This panel does not fit on landscape with two rows of buttons showing,
- // so skip adding the panel (and incrementing view counT) in that case
- val isLandscape =
- context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
- if (isLandscape && nActions > MAX_BUTTONS_LANDSCAPE) {
- return
- }
-
- val view = LayoutInflater.from(context).inflate(R.layout.global_actions_change_panel,
- parent, false)
- val button = view.findViewById<ImageView>(R.id.global_actions_change_button)
- button.setOnClickListener { _ ->
- dismissParent.run()
- activityStarter.postStartActivityDismissingKeyguard(pendingIntent)
- }
- parent.addView(view, 0) // Add to top
- incrementViewCount()
- }
-
- fun shouldShowMessage(): Boolean {
- // This is only relevant for some devices
- val isEligible = context.resources.getBoolean(
- R.bool.global_actions_show_change_info)
- if (!isEligible) {
- return false
- }
-
- val sharedPrefs = context.getSharedPreferences(PREFERENCE, Context.MODE_PRIVATE)
-
- // Only show to users who previously had these items set up
- val viewCount = if (sharedPrefs.contains(KEY_VIEW_COUNT) || hadContent()) {
- sharedPrefs.getInt(KEY_VIEW_COUNT, 0)
- } else {
- -1
- }
-
- // Limit number of times this is displayed
- return viewCount > -1 && viewCount < MAX_VIEW_COUNT
- }
-
- private fun hadContent(): Boolean {
- // Check whether user would have seen content in the power menu that has now moved
- val hadControls = controlsController.getFavorites().size > 0
- val hadCards = walletClient.isWalletFeatureAvailable
- Log.d(TAG, "Previously had controls $hadControls, cards $hadCards")
- return hadControls || hadCards
- }
-
- private fun incrementViewCount() {
- val sharedPrefs = context.getSharedPreferences(PREFERENCE, Context.MODE_PRIVATE)
- val count = sharedPrefs.getInt(KEY_VIEW_COUNT, 0)
- sharedPrefs.edit().putInt(KEY_VIEW_COUNT, count + 1).apply()
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index da09793..07a4988 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -44,6 +44,7 @@
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
@@ -54,9 +55,7 @@
import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_WINDOW_STATE;
import static com.android.systemui.statusbar.phone.StatusBar.dumpBarTransitions;
-import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.IdRes;
-import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
@@ -101,7 +100,6 @@
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
import android.view.inputmethod.InputMethodManager;
import androidx.annotation.VisibleForTesting;
@@ -129,6 +127,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
+import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.AutoHideUiElement;
@@ -149,7 +148,6 @@
import com.android.wm.shell.pip.Pip;
import java.io.PrintWriter;
-import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.function.Consumer;
@@ -160,8 +158,7 @@
* Contains logic for a navigation bar view.
*/
public class NavigationBar implements View.OnAttachStateChangeListener,
- Callbacks, NavigationModeController.ModeChangedListener,
- AccessibilityButtonModeObserver.ModeChangedListener {
+ Callbacks, NavigationModeController.ModeChangedListener {
public static final String TAG = "NavigationBar";
private static final boolean DEBUG = false;
@@ -178,7 +175,6 @@
private final Context mContext;
private final WindowManager mWindowManager;
private final AccessibilityManager mAccessibilityManager;
- private final AccessibilityManagerWrapper mAccessibilityManagerWrapper;
private final DeviceProvisionedController mDeviceProvisionedController;
private final StatusBarStateController mStatusBarStateController;
private final MetricsLogger mMetricsLogger;
@@ -199,6 +195,7 @@
private final Handler mHandler;
private final NavigationBarOverlayController mNavbarOverlayController;
private final UiEventLogger mUiEventLogger;
+ private final NavigationBarA11yHelper mNavigationBarA11yHelper;
private Bundle mSavedState;
private NavigationBarView mNavigationBarView;
@@ -232,7 +229,6 @@
private boolean mTransientShown;
private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
- private int mA11yBtnMode;
private LightBarController mLightBarController;
private AutoHideController mAutoHideController;
@@ -459,11 +455,11 @@
SystemActions systemActions,
@Main Handler mainHandler,
NavigationBarOverlayController navbarOverlayController,
- UiEventLogger uiEventLogger) {
+ UiEventLogger uiEventLogger,
+ NavigationBarA11yHelper navigationBarA11yHelper) {
mContext = context;
mWindowManager = windowManager;
mAccessibilityManager = accessibilityManager;
- mAccessibilityManagerWrapper = accessibilityManagerWrapper;
mDeviceProvisionedController = deviceProvisionedController;
mStatusBarStateController = statusBarStateController;
mMetricsLogger = metricsLogger;
@@ -484,10 +480,8 @@
mHandler = mainHandler;
mNavbarOverlayController = navbarOverlayController;
mUiEventLogger = uiEventLogger;
-
+ mNavigationBarA11yHelper = navigationBarA11yHelper;
mNavBarMode = mNavigationModeController.addListener(this);
- mAccessibilityButtonModeObserver.addListener(this);
- mA11yBtnMode = mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode();
}
public NavigationBarView getView() {
@@ -579,9 +573,8 @@
mContext.getSystemService(WindowManager.class).removeViewImmediate(
mNavigationBarView.getRootView());
mNavigationModeController.removeListener(this);
- mAccessibilityButtonModeObserver.removeListener(this);
- mAccessibilityManagerWrapper.removeCallback(mAccessibilityListener);
+ mNavigationBarA11yHelper.removeA11yEventListener(mAccessibilityListener);
mContentResolver.unregisterContentObserver(mAssistContentObserver);
mDeviceProvisionedController.removeCallback(mUserSetupListener);
@@ -602,7 +595,7 @@
mNavigationBarView.setWindowVisible(isNavBarWindowVisible());
mNavigationBarView.setBehavior(mBehavior);
- mAccessibilityManagerWrapper.addCallback(mAccessibilityListener);
+ mNavigationBarA11yHelper.registerA11yEventListener(mAccessibilityListener);
mSplitScreenOptional.ifPresent(mNavigationBarView::registerDockedListener);
mPipOptional.ifPresent(mNavigationBarView::registerPipExclusionBoundsChangeListener);
@@ -862,26 +855,8 @@
return;
}
boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
- int hints = mNavigationIconHints;
- switch (backDisposition) {
- case InputMethodService.BACK_DISPOSITION_DEFAULT:
- case InputMethodService.BACK_DISPOSITION_WILL_NOT_DISMISS:
- case InputMethodService.BACK_DISPOSITION_WILL_DISMISS:
- if (imeShown) {
- hints |= NAVIGATION_HINT_BACK_ALT;
- } else {
- hints &= ~NAVIGATION_HINT_BACK_ALT;
- }
- break;
- case InputMethodService.BACK_DISPOSITION_ADJUST_NOTHING:
- hints &= ~NAVIGATION_HINT_BACK_ALT;
- break;
- }
- if (showImeSwitcher) {
- hints |= NAVIGATION_HINT_IME_SHOWN;
- } else {
- hints &= ~NAVIGATION_HINT_IME_SHOWN;
- }
+ int hints = Utilities.calculateBackDispositionHints(mNavigationIconHints, backDisposition,
+ imeShown, showImeSwitcher);
if (hints == mNavigationIconHints) return;
mNavigationIconHints = hints;
@@ -1141,7 +1116,7 @@
ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton();
accessibilityButton.setOnClickListener(this::onAccessibilityClick);
accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick);
- updateAccessibilityServicesState(mAccessibilityManager);
+ updateAccessibilityServicesState();
ButtonDispatcher imeSwitcherButton = mNavigationBarView.getImeSwitchButton();
imeSwitcherButton.setOnClickListener(this::onImeSwitcherClick);
@@ -1362,9 +1337,8 @@
return true;
}
- void updateAccessibilityServicesState(AccessibilityManager accessibilityManager) {
- boolean[] feedbackEnabled = new boolean[1];
- int a11yFlags = getA11yButtonState(feedbackEnabled);
+ void updateAccessibilityServicesState() {
+ int a11yFlags = mNavigationBarA11yHelper.getA11yButtonState();
boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
@@ -1382,7 +1356,7 @@
public void updateSystemUiStateFlags(int a11yFlags) {
if (a11yFlags < 0) {
- a11yFlags = getA11yButtonState(null);
+ a11yFlags = mNavigationBarA11yHelper.getA11yButtonState();
}
boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
@@ -1392,6 +1366,8 @@
.setFlag(SYSUI_STATE_NAV_BAR_HIDDEN, !isNavBarWindowVisible())
.setFlag(SYSUI_STATE_IME_SHOWING,
(mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0)
+ .setFlag(SYSUI_STATE_IME_SWITCHER_SHOWING,
+ (mNavigationIconHints & NAVIGATION_HINT_IME_SHOWN) != 0)
.setFlag(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
allowSystemGestureIgnoringBarVisibility())
.commitUpdate(mDisplayId);
@@ -1407,44 +1383,6 @@
}
}
- /**
- * Returns the system UI flags corresponding the the current accessibility button state
- *
- * @param outFeedbackEnabled if non-null, sets it to true if accessibility feedback is enabled.
- */
- public int getA11yButtonState(@Nullable boolean[] outFeedbackEnabled) {
- boolean feedbackEnabled = false;
- // AccessibilityManagerService resolves services for the current user since the local
- // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission
- final List<AccessibilityServiceInfo> services =
- mAccessibilityManager.getEnabledAccessibilityServiceList(
- AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
- final List<String> a11yButtonTargets =
- mAccessibilityManager.getAccessibilityShortcutTargets(
- AccessibilityManager.ACCESSIBILITY_BUTTON);
- final int requestingServices = a11yButtonTargets.size();
- for (int i = services.size() - 1; i >= 0; --i) {
- AccessibilityServiceInfo info = services.get(i);
- if (info.feedbackType != 0 && info.feedbackType !=
- AccessibilityServiceInfo.FEEDBACK_GENERIC) {
- feedbackEnabled = true;
- }
- }
-
- if (outFeedbackEnabled != null) {
- outFeedbackEnabled[0] = feedbackEnabled;
- }
-
- // If accessibility button is floating menu mode, click and long click state should be
- // disabled.
- if (mA11yBtnMode == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
- return 0;
- }
-
- return (requestingServices >= 1 ? SYSUI_STATE_A11Y_BUTTON_CLICKABLE : 0)
- | (requestingServices >= 2 ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0);
- }
-
private void updateAssistantEntrypoints() {
mAssistantAvailable = mAssistManagerLazy.get()
.getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
@@ -1544,12 +1482,6 @@
}
}
- @Override
- public void onAccessibilityButtonModeChanged(int mode) {
- mA11yBtnMode = mode;
- updateAccessibilityServicesState(mAccessibilityManager);
- }
-
public void disableAnimationsDuringHide(long delay) {
mNavigationBarView.setLayoutTransitionsEnabled(false);
mHandler.postDelayed(mEnableLayoutTransitions,
@@ -1574,7 +1506,7 @@
mNavigationBarView.getBarTransitions().finishAnimations();
}
- private final AccessibilityServicesStateChangeListener mAccessibilityListener =
+ private final NavigationBarA11yHelper.NavA11yEventListener mAccessibilityListener =
this::updateAccessibilityServicesState;
private boolean canShowSecondaryHandle() {
@@ -1590,6 +1522,13 @@
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ // This receiver is unregistered when the view is detached, but on devices with multiple
+ // displays, it can sometimes still receive an ACTION_SCREEN_ON/ACTION_SCREEN_OFF on
+ // display switch, after it was detached, so this null check ensures no crash in that
+ // scenario.
+ if (mNavigationBarView == null) {
+ return;
+ }
String action = intent.getAction();
if (Intent.ACTION_SCREEN_OFF.equals(action)
|| Intent.ACTION_SCREEN_ON.equals(action)) {
@@ -1598,7 +1537,7 @@
}
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
// The accessibility settings may be different for the new user
- updateAccessibilityServicesState(mAccessibilityManager);
+ updateAccessibilityServicesState();
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java
new file mode 100644
index 0000000..35e1a9d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java
@@ -0,0 +1,89 @@
+package com.android.systemui.navigationbar;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
+
+import android.view.accessibility.AccessibilityManager;
+
+import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+
+/**
+ * Extracts shared elements of a11y necessary between navbar and taskbar delegate
+ */
+@SysUISingleton
+public final class NavigationBarA11yHelper implements
+ AccessibilityButtonModeObserver.ModeChangedListener {
+ private int mA11yBtnMode;
+ private final AccessibilityManager mAccessibilityManager;
+ private final List<NavA11yEventListener> mA11yEventListeners = new ArrayList<>();
+
+ @Inject
+ public NavigationBarA11yHelper(AccessibilityManager accessibilityManager,
+ AccessibilityManagerWrapper accessibilityManagerWrapper,
+ AccessibilityButtonModeObserver accessibilityButtonModeObserver) {
+ mAccessibilityManager = accessibilityManager;
+ mA11yBtnMode = accessibilityButtonModeObserver.getCurrentAccessibilityButtonMode();
+ accessibilityManagerWrapper.addCallback(
+ accessibilityManager1 -> NavigationBarA11yHelper.this.dispatchEventUpdate());
+ accessibilityButtonModeObserver.addListener(this);
+ }
+
+ public void registerA11yEventListener(NavA11yEventListener listener) {
+ mA11yEventListeners.add(listener);
+ }
+
+ public void removeA11yEventListener(NavA11yEventListener listener) {
+ mA11yEventListeners.remove(listener);
+ }
+
+ private void dispatchEventUpdate() {
+ for (NavA11yEventListener listener : mA11yEventListeners) {
+ listener.updateAccessibilityServicesState();
+ }
+ }
+
+ @Override
+ public void onAccessibilityButtonModeChanged(int mode) {
+ mA11yBtnMode = mode;
+ dispatchEventUpdate();
+ }
+
+ /**
+ * See {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_CLICKABLE} and
+ * {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE}
+ *
+ * @return the a11y button clickable and long_clickable states, or 0 if there is no
+ * a11y button in the navbar
+ */
+ public int getA11yButtonState() {
+ // AccessibilityManagerService resolves services for the current user since the local
+ // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission
+ final List<String> a11yButtonTargets =
+ mAccessibilityManager.getAccessibilityShortcutTargets(
+ AccessibilityManager.ACCESSIBILITY_BUTTON);
+ final int requestingServices = a11yButtonTargets.size();
+
+ // If accessibility button is floating menu mode, click and long click state should be
+ // disabled.
+ if (mA11yBtnMode == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
+ return 0;
+ }
+
+ return (requestingServices >= 1 ? SYSUI_STATE_A11Y_BUTTON_CLICKABLE : 0)
+ | (requestingServices >= 2 ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0);
+ }
+
+ public interface NavA11yEventListener {
+ void updateAccessibilityServicesState();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 8b5a537..30eb645 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -111,6 +111,7 @@
private final SystemActions mSystemActions;
private final UiEventLogger mUiEventLogger;
private final Handler mHandler;
+ private final NavigationBarA11yHelper mNavigationBarA11yHelper;
private final DisplayManager mDisplayManager;
private final NavigationBarOverlayController mNavBarOverlayController;
private final TaskbarDelegate mTaskbarDelegate;
@@ -151,7 +152,8 @@
@Main Handler mainHandler,
UiEventLogger uiEventLogger,
NavigationBarOverlayController navBarOverlayController,
- ConfigurationController configurationController) {
+ ConfigurationController configurationController,
+ NavigationBarA11yHelper navigationBarA11yHelper) {
mContext = context;
mWindowManager = windowManager;
mAssistManagerLazy = assistManagerLazy;
@@ -175,6 +177,7 @@
mSystemActions = systemActions;
mUiEventLogger = uiEventLogger;
mHandler = mainHandler;
+ mNavigationBarA11yHelper = navigationBarA11yHelper;
mDisplayManager = mContext.getSystemService(DisplayManager.class);
commandQueue.addCallback(this);
configurationController.addCallback(this);
@@ -182,7 +185,8 @@
mNavBarOverlayController = navBarOverlayController;
mNavMode = mNavigationModeController.addListener(this);
mNavigationModeController.addListener(this);
- mTaskbarDelegate = new TaskbarDelegate(mOverviewProxyService);
+ mTaskbarDelegate = new TaskbarDelegate(mOverviewProxyService,
+ navigationBarA11yHelper, mSysUiFlagsContainer);
mIsTablet = isTablet(mContext.getResources().getConfiguration());
}
@@ -241,10 +245,12 @@
// Remove navigation bar when taskbar is showing, currently only for 3 button mode
removeNavigationBar(mContext.getDisplayId());
mCommandQueue.addCallback(mTaskbarDelegate);
+ mTaskbarDelegate.init(mContext.getDisplayId());
} else if (mNavigationBars.get(mContext.getDisplayId()) == null) {
// Add navigation bar after taskbar goes away
createNavigationBar(mContext.getDisplay(), null, null);
mCommandQueue.removeCallback(mTaskbarDelegate);
+ mTaskbarDelegate.destroy();
}
return true;
@@ -361,7 +367,8 @@
mSystemActions,
mHandler,
mNavBarOverlayController,
- mUiEventLogger);
+ mUiEventLogger,
+ mNavigationBarA11yHelper);
mNavigationBars.put(displayId, navBar);
View navigationBarView = navBar.createView(savedState);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java
index 649ac87..a5b7911 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java
@@ -47,6 +47,8 @@
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.navigationbar.buttons.KeyButtonDrawable;
+import com.android.systemui.shared.recents.utilities.Utilities;
+import com.android.systemui.shared.recents.utilities.ViewRippler;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
@@ -311,7 +313,7 @@
// Prepare to show the navbar icon by updating the icon style to change anim params
mLastRotationSuggestion = rotation; // Remember rotation for click
- final boolean rotationCCW = isRotationAnimationCCW(windowRotation, rotation);
+ final boolean rotationCCW = Utilities.isRotationAnimationCCW(windowRotation, rotation);
if (windowRotation == Surface.ROTATION_0 || windowRotation == Surface.ROTATION_180) {
mIconResId = rotationCCW
? R.drawable.ic_sysbar_rotate_button_ccw_start_90
@@ -431,23 +433,6 @@
return rotation == NATURAL_ROTATION;
}
- private boolean isRotationAnimationCCW(int from, int to) {
- // All 180deg WM rotation animations are CCW, match that
- if (from == Surface.ROTATION_0 && to == Surface.ROTATION_90) return false;
- if (from == Surface.ROTATION_0 && to == Surface.ROTATION_180) return true; //180d so CCW
- if (from == Surface.ROTATION_0 && to == Surface.ROTATION_270) return true;
- if (from == Surface.ROTATION_90 && to == Surface.ROTATION_0) return true;
- if (from == Surface.ROTATION_90 && to == Surface.ROTATION_180) return false;
- if (from == Surface.ROTATION_90 && to == Surface.ROTATION_270) return true; //180d so CCW
- if (from == Surface.ROTATION_180 && to == Surface.ROTATION_0) return true; //180d so CCW
- if (from == Surface.ROTATION_180 && to == Surface.ROTATION_90) return true;
- if (from == Surface.ROTATION_180 && to == Surface.ROTATION_270) return false;
- if (from == Surface.ROTATION_270 && to == Surface.ROTATION_0) return false;
- if (from == Surface.ROTATION_270 && to == Surface.ROTATION_90) return true; //180d so CCW
- if (from == Surface.ROTATION_270 && to == Surface.ROTATION_180) return true;
- return false; // Default
- }
-
private void rescheduleRotationTimeout(final boolean reasonHover) {
// May be called due to a new rotation proposal or a change in hover state
if (reasonHover) {
@@ -520,38 +505,6 @@
}
}
- private class ViewRippler {
- private static final int RIPPLE_OFFSET_MS = 50;
- private static final int RIPPLE_INTERVAL_MS = 2000;
- private View mRoot;
-
- public void start(View root) {
- stop(); // Stop any pending ripple animations
-
- mRoot = root;
-
- // Schedule pending ripples, offset the 1st to avoid problems with visibility change
- mRoot.postOnAnimationDelayed(mRipple, RIPPLE_OFFSET_MS);
- mRoot.postOnAnimationDelayed(mRipple, RIPPLE_INTERVAL_MS);
- mRoot.postOnAnimationDelayed(mRipple, 2 * RIPPLE_INTERVAL_MS);
- mRoot.postOnAnimationDelayed(mRipple, 3 * RIPPLE_INTERVAL_MS);
- mRoot.postOnAnimationDelayed(mRipple, 4 * RIPPLE_INTERVAL_MS);
- }
-
- public void stop() {
- if (mRoot != null) mRoot.removeCallbacks(mRipple);
- }
-
- private final Runnable mRipple = new Runnable() {
- @Override
- public void run() { // Cause the ripple to fire via false presses
- if (!mRoot.isAttachedToWindow()) return;
- mRoot.setPressed(true /* pressed */);
- mRoot.setPressed(false /* pressed */);
- }
- };
- }
-
enum RotationButtonEvent implements UiEventLogger.UiEventEnum {
@UiEvent(doc = "The rotation button was shown")
ROTATION_SUGGESTION_SHOWN(206),
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 03147d8..e9674c9 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -16,23 +16,92 @@
package com.android.systemui.navigationbar;
+import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
+
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
+
+import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
+import com.android.internal.view.AppearanceRegion;
+import com.android.systemui.model.SysUiState;
import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.statusbar.CommandQueue;
public class TaskbarDelegate implements CommandQueue.Callbacks {
private final OverviewProxyService mOverviewProxyService;
+ private final NavigationBarA11yHelper mNavigationBarA11yHelper;
+ private final SysUiState mSysUiState;
+ private int mDisplayId;
+ private int mNavigationIconHints;
+ private final NavigationBarA11yHelper.NavA11yEventListener mNavA11yEventListener =
+ this::updateSysuiFlags;
- public TaskbarDelegate(OverviewProxyService overviewProxyService) {
+ public TaskbarDelegate(OverviewProxyService overviewProxyService,
+ NavigationBarA11yHelper navigationBarA11yHelper,
+ SysUiState sysUiState) {
mOverviewProxyService = overviewProxyService;
+ mNavigationBarA11yHelper = navigationBarA11yHelper;
+ mSysUiState = sysUiState;
+ }
+
+ public void destroy() {
+ mNavigationBarA11yHelper.removeA11yEventListener(mNavA11yEventListener);
+ }
+
+ public void init(int displayId) {
+ mDisplayId = displayId;
+ mNavigationBarA11yHelper.registerA11yEventListener(mNavA11yEventListener);
+ // Set initial state for any listeners
+ updateSysuiFlags();
+ }
+
+ private void updateSysuiFlags() {
+ int a11yFlags = mNavigationBarA11yHelper.getA11yButtonState();
+ boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
+ boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
+
+ mSysUiState.setFlag(SYSUI_STATE_A11Y_BUTTON_CLICKABLE, clickable)
+ .setFlag(SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE, longClickable)
+ .setFlag(SYSUI_STATE_IME_SHOWING,
+ (mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0)
+ .setFlag(SYSUI_STATE_IME_SWITCHER_SHOWING,
+ (mNavigationIconHints & NAVIGATION_HINT_IME_SHOWN) != 0)
+ .commitUpdate(mDisplayId);
}
@Override
public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
boolean showImeSwitcher) {
- mOverviewProxyService.notifyImeWindowStatus(displayId, token, vis, backDisposition,
- showImeSwitcher);
+ boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
+ int hints = Utilities.calculateBackDispositionHints(mNavigationIconHints, backDisposition,
+ imeShown, showImeSwitcher);
+ if (hints != mNavigationIconHints) {
+ mNavigationIconHints = hints;
+ updateSysuiFlags();
+ }
+ }
+
+ @Override
+ public void onRotationProposal(int rotation, boolean isValid) {
+ mOverviewProxyService.onRotationProposal(rotation, isValid);
+ }
+
+ @Override
+ public void disable(int displayId, int state1, int state2, boolean animate) {
+ mOverviewProxyService.disable(displayId, state1, state2, animate);
+ }
+
+ @Override
+ public void onSystemBarAttributesChanged(int displayId, int appearance,
+ AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, int behavior,
+ boolean isFullscreen) {
+ mOverviewProxyService.onSystemBarAttributesChanged(displayId, behavior);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
index 1d2e747..eec69f98 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
@@ -28,7 +28,7 @@
appsAndTypes = itemsList.groupBy({ it.application }, { it.privacyType })
.toList()
.sortedWith(compareBy({ -it.second.size }, // Sort by number of AppOps
- { it.second.min() })) // Sort by "smallest" AppOpp (Location is largest)
+ { it.second.minOrNull() })) // Sort by "smallest" AppOpp (Location is largest)
types = itemsList.map { it.privacyType }.distinct().sorted()
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index cb0c411..e8321ab 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -967,19 +967,40 @@
}
}
- public void notifyImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
- boolean showImeSwitcher) {
+ public void onRotationProposal(int rotation, boolean isValid) {
try {
if (mOverviewProxy != null) {
- mOverviewProxy.onImeWindowStatusChanged(displayId, token, vis, backDisposition,
- showImeSwitcher);
+ mOverviewProxy.onRotationProposal(rotation, isValid);
} else {
- Log.e(TAG_OPS, "Failed to get overview proxy for setting IME status.");
+ Log.e(TAG_OPS, "Failed to get overview proxy for proposing rotation.");
}
} catch (RemoteException e) {
- Log.e(TAG_OPS, "Failed to call notifyImeWindowStatus()", e);
+ Log.e(TAG_OPS, "Failed to call onRotationProposal()", e);
}
+ }
+ public void disable(int displayId, int state1, int state2, boolean animate) {
+ try {
+ if (mOverviewProxy != null) {
+ mOverviewProxy.disable(displayId, state1, state2, animate);
+ } else {
+ Log.e(TAG_OPS, "Failed to get overview proxy for disable flags.");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG_OPS, "Failed to call disable()", e);
+ }
+ }
+
+ public void onSystemBarAttributesChanged(int displayId, int behavior) {
+ try {
+ if (mOverviewProxy != null) {
+ mOverviewProxy.onSystemBarAttributesChanged(displayId, behavior);
+ } else {
+ Log.e(TAG_OPS, "Failed to get overview proxy for system bar attr change.");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG_OPS, "Failed to call onSystemBarAttributesChanged()", e);
+ }
}
private void updateEnabledState() {
@@ -1030,7 +1051,5 @@
default void onAssistantProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {}
default void onAssistantGestureCompletion(float velocity) {}
default void startAssistant(Bundle bundle) {}
- default void onImeWindowStatusChanged(int displayId, IBinder token, int vis,
- int backDisposition, boolean showImeSwitcher) {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 8277fae..5006367 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -5263,6 +5263,10 @@
mSwipeHelper.resetExposedMenuView(animate, force);
}
+ boolean isUsingSplitNotificationShade() {
+ return mShouldUseSplitNotificationShade;
+ }
+
static boolean matchesSelection(
ExpandableNotificationRow row,
@SelectedRows int selection) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index dec9888..10a1fda 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -1105,11 +1105,16 @@
/**
* Update whether we should show the empty shade view (no notifications in the shade).
* If so, send the update to our view.
+ *
+ * When in split mode, notifications are always visible regardless of the state of the
+ * QuickSettings panel. That being the case, empty view is always shown if the other conditions
+ * are true.
*/
public void updateShowEmptyShadeView() {
mShowEmptyShadeView = mBarState != KEYGUARD
- && !mView.isQsExpanded()
+ && (!mView.isQsExpanded() || mView.isUsingSplitNotificationShade())
&& mView.getVisibleNotificationCount() == 0;
+
mView.updateEmptyShadeView(
mShowEmptyShadeView,
mZenModeController.areNotificationsHiddenInShade());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index b3569d0..f29ec8ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -23,6 +23,8 @@
import static androidx.constraintlayout.widget.ConstraintSet.START;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
+import static com.android.keyguard.KeyguardClockSwitch.LARGE;
+import static com.android.keyguard.KeyguardClockSwitch.SMALL;
import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
@@ -1213,7 +1215,14 @@
updateClockAppearance();
}
if (!onKeyguard) {
- stackScrollerPadding = getUnlockedStackScrollerPadding();
+ if (mShouldUseSplitNotificationShade) {
+ // Quick settings are not on the top of the notifications
+ // when in split shade mode (they are on the left side),
+ // so we should not add a padding for them
+ stackScrollerPadding = 0;
+ } else {
+ stackScrollerPadding = getUnlockedStackScrollerPadding();
+ }
} else {
stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded;
}
@@ -1234,7 +1243,11 @@
boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
final boolean hasVisibleNotifications = mNotificationStackScrollLayoutController
.getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia();
- mKeyguardStatusViewController.setHasVisibleNotifications(hasVisibleNotifications);
+ if (hasVisibleNotifications && !mShouldUseSplitNotificationShade) {
+ mKeyguardStatusViewController.displayClock(SMALL);
+ } else {
+ mKeyguardStatusViewController.displayClock(LARGE);
+ }
int userIconHeight = mKeyguardQsUserSwitchController != null
? mKeyguardQsUserSwitchController.getUserIconHeight() : 0;
float expandedFraction =
@@ -2216,10 +2229,16 @@
int right = 0;
final int qsPanelBottomY = calculateQsBottomPosition(computeQsExpansionFraction());
- final boolean visible = (computeQsExpansionFraction() > 0 || qsPanelBottomY > 0)
- && !mShouldUseSplitNotificationShade;
+ final boolean quickSettingsVisible = computeQsExpansionFraction() > 0 || qsPanelBottomY > 0;
- if (!mShouldUseSplitNotificationShade) {
+ if (mShouldUseSplitNotificationShade && quickSettingsVisible) {
+ mAmbientState.setNotificationScrimTop(mSplitShadeNotificationsTopPadding);
+
+ top = Math.max(0, Math.min(qsPanelBottomY, mSplitShadeNotificationsTopPadding));
+ bottom = mNotificationStackScrollLayoutController.getHeight();
+ left = mNotificationStackScrollLayoutController.getLeft();
+ right = mNotificationStackScrollLayoutController.getRight();
+ } else {
if (mTransitioningToFullShadeProgress > 0.0f) {
// If we're transitioning, let's use the actual value. The else case
// can be wrong during transitions when waiting for the keyguard to unlock
@@ -2234,16 +2253,11 @@
// notification bounds should take full screen width regardless of insets
left = 0;
right = getView().getRight() + mDisplayRightInset;
- } else if (qsPanelBottomY > 0) { // so bounds are empty on lockscreen
- mAmbientState.setNotificationScrimTop(mSplitShadeNotificationsTopPadding);
- top = Math.min(qsPanelBottomY, mSplitShadeNotificationsTopPadding);
- bottom = mNotificationStackScrollLayoutController.getHeight();
- left = mNotificationStackScrollLayoutController.getLeft();
- right = mNotificationStackScrollLayoutController.getRight();
}
// top should never be lower than bottom, otherwise it will be invisible.
top = Math.min(top, bottom);
- applyQSClippingBounds(left, top, right, bottom, visible);
+ applyQSClippingBounds(left, top, right, bottom,
+ quickSettingsVisible && !mShouldUseSplitNotificationShade);
}
private void applyQSClippingBounds(int left, int top, int right, int bottom,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 7a1e5cf..18be53f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -359,21 +359,7 @@
mAnimateChange = state.getAnimateChange();
mAnimationDuration = state.getAnimationDuration();
- mInFrontTint = state.getFrontTint();
- mBehindTint = state.getBehindTint();
- mNotificationsTint = state.getNotifTint();
- mBubbleTint = state.getBubbleTint();
-
- mInFrontAlpha = state.getFrontAlpha();
- mBehindAlpha = state.getBehindAlpha();
- mBubbleAlpha = state.getBubbleAlpha();
- mNotificationsAlpha = state.getNotifAlpha();
- if (isNaN(mBehindAlpha) || isNaN(mInFrontAlpha) || isNaN(mNotificationsAlpha)) {
- throw new IllegalStateException("Scrim opacity is NaN for state: " + state + ", front: "
- + mInFrontAlpha + ", back: " + mBehindAlpha + ", notif: "
- + mNotificationsAlpha);
- }
- applyStateToAlpha();
+ applyState();
// Scrim might acquire focus when user is navigating with a D-pad or a keyboard.
// We need to disable focus otherwise AOD would end up with a gray overlay.
@@ -637,7 +623,19 @@
}
}
- private void applyStateToAlpha() {
+ private void applyState() {
+ mInFrontTint = mState.getFrontTint();
+ mBehindTint = mState.getBehindTint();
+ mNotificationsTint = mState.getNotifTint();
+ mBubbleTint = mState.getBubbleTint();
+
+ mInFrontAlpha = mState.getFrontAlpha();
+ mBehindAlpha = mState.getBehindAlpha();
+ mBubbleAlpha = mState.getBubbleAlpha();
+ mNotificationsAlpha = mState.getNotifAlpha();
+
+ assertAlphasValid();
+
if (!mExpansionAffectsAlpha) {
return;
}
@@ -695,6 +693,11 @@
mBehindTint = behindTint;
}
}
+
+ assertAlphasValid();
+ }
+
+ private void assertAlphasValid() {
if (isNaN(mBehindAlpha) || isNaN(mInFrontAlpha) || isNaN(mNotificationsAlpha)) {
throw new IllegalStateException("Scrim opacity is NaN for state: " + mState
+ ", front: " + mInFrontAlpha + ", back: " + mBehindAlpha + ", notif: "
@@ -734,7 +737,7 @@
private void applyAndDispatchState() {
- applyStateToAlpha();
+ applyState();
if (mUpdatePending) {
return;
}
@@ -1203,6 +1206,7 @@
pw.println(" ScrimController: ");
pw.print(" state: ");
pw.println(mState);
+ pw.println(" mClipQsScrim = " + mState.mClipQsScrim);
pw.print(" frontScrim:");
pw.print(" viewAlpha=");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index c7efcb2..d5607f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -921,6 +921,9 @@
mStatusBar.setBouncerShowing(bouncerShowing);
}
+ if (occluded != mLastOccluded || mFirstUpdate) {
+ mKeyguardUpdateManager.onKeyguardOccludedChanged(occluded);
+ }
if ((showing && !occluded) != (mLastShowing && !mLastOccluded) || mFirstUpdate) {
mKeyguardUpdateManager.onKeyguardVisibilityChanged(showing && !occluded);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index efeeac6..7136962 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -73,11 +73,10 @@
import androidx.annotation.NonNull;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
-import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
-import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.Dependency;
@@ -158,7 +157,9 @@
@UiEvent(doc = "User sent data through the notification remote input view")
NOTIFICATION_REMOTE_INPUT_SEND(797),
@UiEvent(doc = "Failed attempt to send data through the notification remote input view")
- NOTIFICATION_REMOTE_INPUT_FAILURE(798);
+ NOTIFICATION_REMOTE_INPUT_FAILURE(798),
+ @UiEvent(doc = "User attached an image to the remote input view")
+ NOTIFICATION_REMOTE_INPUT_ATTACH_IMAGE(825);
private final int mId;
NotificationRemoteInputEvent(int id) {
@@ -292,7 +293,8 @@
});
}
- private void setAttachment(ContentInfo item) {
+ @VisibleForTesting
+ protected void setAttachment(ContentInfo item) {
if (mAttachment != null) {
// We need to release permissions when sending the attachment to the target
// app or if it is deleted by the user. When sending to the target app, we
@@ -314,6 +316,10 @@
attachment.setVisibility(GONE);
} else {
attachment.setVisibility(VISIBLE);
+ mUiEventLogger.logWithInstanceId(
+ NotificationRemoteInputEvent.NOTIFICATION_REMOTE_INPUT_ATTACH_IMAGE,
+ mEntry.getSbn().getUid(), mEntry.getSbn().getPackageName(),
+ mEntry.getSbn().getInstanceId());
}
updateSendButton();
}
@@ -420,8 +426,6 @@
mEntry.getSbn().getPackageName(),
mEntry.getSbn().getUser().getIdentifier());
- MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_SEND,
- mEntry.getSbn().getPackageName());
mUiEventLogger.logWithInstanceId(
NotificationRemoteInputEvent.NOTIFICATION_REMOTE_INPUT_SEND,
mEntry.getSbn().getUid(), mEntry.getSbn().getPackageName(),
@@ -430,8 +434,6 @@
mPendingIntent.send(mContext, 0, intent);
} catch (PendingIntent.CanceledException e) {
Log.i(TAG, "Unable to send remote input result", e);
- MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_FAIL,
- mEntry.getSbn().getPackageName());
mUiEventLogger.logWithInstanceId(
NotificationRemoteInputEvent.NOTIFICATION_REMOTE_INPUT_FAILURE,
mEntry.getSbn().getUid(), mEntry.getSbn().getPackageName(),
@@ -508,8 +510,6 @@
mRemoteInputQuickSettingsDisabler.setRemoteInputActive(false);
if (logClose) {
- MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_CLOSE,
- mEntry.getSbn().getPackageName());
mUiEventLogger.logWithInstanceId(
NotificationRemoteInputEvent.NOTIFICATION_REMOTE_INPUT_CLOSE,
mEntry.getSbn().getUid(), mEntry.getSbn().getPackageName(),
@@ -591,8 +591,6 @@
}
public void focus() {
- MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_OPEN,
- mEntry.getSbn().getPackageName());
mUiEventLogger.logWithInstanceId(
NotificationRemoteInputEvent.NOTIFICATION_REMOTE_INPUT_OPEN,
mEntry.getSbn().getUid(), mEntry.getSbn().getPackageName(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 5c44017..e2129bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -22,6 +22,7 @@
import static com.android.systemui.DejankUtils.whitelistIpcs;
import android.app.ActivityManager;
+import android.app.AlertDialog;
import android.app.Dialog;
import android.app.IActivityTaskManager;
import android.content.BroadcastReceiver;
@@ -67,9 +68,11 @@
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.qs.QSUserSwitcherEvent;
import com.android.systemui.qs.tiles.UserDetailView;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.user.CreateUserActivity;
+import com.android.systemui.util.settings.SecureSettings;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -98,9 +101,12 @@
private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
protected final Context mContext;
+ protected final UserTracker mUserTracker;
protected final UserManager mUserManager;
+ private final ContentObserver mSettingsObserver;
private final ArrayList<WeakReference<BaseUserAdapter>> mAdapters = new ArrayList<>();
- private final GuestResumeSessionReceiver mGuestResumeSessionReceiver;
+ @VisibleForTesting
+ final GuestResumeSessionReceiver mGuestResumeSessionReceiver;
private final KeyguardStateController mKeyguardStateController;
protected final Handler mHandler;
private final ActivityStarter mActivityStarter;
@@ -109,8 +115,10 @@
private final IActivityTaskManager mActivityTaskManager;
private ArrayList<UserRecord> mUsers = new ArrayList<>();
- private Dialog mExitGuestDialog;
- private Dialog mAddUserDialog;
+ @VisibleForTesting
+ AlertDialog mExitGuestDialog;
+ @VisibleForTesting
+ Dialog mAddUserDialog;
private int mLastNonGuestUser = UserHandle.USER_SYSTEM;
private boolean mResumeUserOnGuestLogout = true;
private boolean mSimpleUserSwitcher;
@@ -125,17 +133,21 @@
public final DetailAdapter mUserDetailAdapter;
@Inject
- public UserSwitcherController(Context context, KeyguardStateController keyguardStateController,
+ public UserSwitcherController(Context context, UserManager userManager, UserTracker userTracker,
+ KeyguardStateController keyguardStateController,
@Main Handler handler, ActivityStarter activityStarter,
BroadcastDispatcher broadcastDispatcher, UiEventLogger uiEventLogger,
TelephonyListenerManager telephonyListenerManager,
- IActivityTaskManager activityTaskManager, UserDetailAdapter userDetailAdapter) {
+ IActivityTaskManager activityTaskManager, UserDetailAdapter userDetailAdapter,
+ SecureSettings secureSettings) {
mContext = context;
+ mUserTracker = userTracker;
mBroadcastDispatcher = broadcastDispatcher;
mTelephonyListenerManager = telephonyListenerManager;
mActivityTaskManager = activityTaskManager;
mUiEventLogger = uiEventLogger;
- mGuestResumeSessionReceiver = new GuestResumeSessionReceiver(mUiEventLogger);
+ mGuestResumeSessionReceiver = new GuestResumeSessionReceiver(
+ mUserTracker, mUiEventLogger, secureSettings);
mUserDetailAdapter = userDetailAdapter;
if (!UserManager.isGuestUserEphemeral()) {
mGuestResumeSessionReceiver.register(mBroadcastDispatcher);
@@ -143,7 +155,7 @@
mKeyguardStateController = keyguardStateController;
mHandler = handler;
mActivityStarter = activityStarter;
- mUserManager = UserManager.get(context);
+ mUserManager = userManager;
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_ADDED);
filter.addAction(Intent.ACTION_USER_REMOVED);
@@ -162,6 +174,14 @@
mContext.registerReceiverAsUser(mReceiver, UserHandle.SYSTEM, filter,
PERMISSION_SELF, null /* scheduler */);
+ mSettingsObserver = new ContentObserver(mHandler) {
+ public void onChange(boolean selfChange) {
+ mSimpleUserSwitcher = shouldUseSimpleUserSwitcher();
+ mAddUsersFromLockScreen = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.ADD_USERS_WHEN_LOCKED, 0) != 0;
+ refreshUsers(UserHandle.USER_NULL);
+ };
+ };
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(SIMPLE_USER_SWITCHER_GLOBAL_SETTING), true,
mSettingsObserver);
@@ -223,11 +243,11 @@
return null;
}
ArrayList<UserRecord> records = new ArrayList<>(infos.size());
- int currentId = ActivityManager.getCurrentUser();
+ int currentId = mUserTracker.getUserId();
// Check user switchability of the foreground user since SystemUI is running in
// User 0
boolean canSwitchUsers = mUserManager.getUserSwitchability(
- UserHandle.of(ActivityManager.getCurrentUser())) == SWITCHABILITY_STATUS_OK;
+ UserHandle.of(mUserTracker.getUserId())) == SWITCHABILITY_STATUS_OK;
UserInfo currentUserInfo = null;
UserRecord guestRecord = null;
@@ -355,7 +375,7 @@
}
public void logoutCurrentUser() {
- int currentUser = ActivityManager.getCurrentUser();
+ int currentUser = mUserTracker.getUserId();
if (currentUser != UserHandle.USER_SYSTEM) {
pauseRefreshUsers();
ActivityManager.logoutCurrentUser();
@@ -367,7 +387,7 @@
Log.w(TAG, "User " + userId + " could not removed.");
return;
}
- if (ActivityManager.getCurrentUser() == userId) {
+ if (mUserTracker.getUserId() == userId) {
switchToUserId(UserHandle.USER_SYSTEM);
}
if (mUserManager.removeUser(userId)) {
@@ -375,7 +395,8 @@
}
}
- private void onUserListItemClicked(UserRecord record) {
+ @VisibleForTesting
+ void onUserListItemClicked(UserRecord record) {
int id;
if (record.isGuest && record.info == null) {
// No guest user. Create one.
@@ -401,7 +422,7 @@
id = record.info.id;
}
- int currUserId = ActivityManager.getCurrentUser();
+ int currUserId = mUserTracker.getUserId();
if (currUserId == id) {
if (record.isGuest) {
showExitGuestDialog(id);
@@ -557,15 +578,6 @@
}
};
- private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) {
- public void onChange(boolean selfChange) {
- mSimpleUserSwitcher = shouldUseSimpleUserSwitcher();
- mAddUsersFromLockScreen = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.ADD_USERS_WHEN_LOCKED, 0) != 0;
- refreshUsers(UserHandle.USER_NULL);
- };
- };
-
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("UserSwitcherController state:");
@@ -701,9 +713,9 @@
private void checkIfAddUserDisallowedByAdminOnly(UserRecord record) {
EnforcedAdmin admin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced(mContext,
- UserManager.DISALLOW_ADD_USER, ActivityManager.getCurrentUser());
+ UserManager.DISALLOW_ADD_USER, mUserTracker.getUserId());
if (admin != null && !RestrictedLockUtilsInternal.hasBaseUserRestriction(mContext,
- UserManager.DISALLOW_ADD_USER, ActivityManager.getCurrentUser())) {
+ UserManager.DISALLOW_ADD_USER, mUserTracker.getUserId())) {
record.isDisabledByAdmin = true;
record.enforcedAdmin = admin;
} else {
@@ -906,7 +918,8 @@
}
}
- private final class AddUserDialog extends SystemUIDialog implements
+ @VisibleForTesting
+ final class AddUserDialog extends SystemUIDialog implements
DialogInterface.OnClickListener {
public AddUserDialog(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index ca1f55e..c3e93b7 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -325,6 +325,11 @@
mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
+ // All wallpaper color and keyguard logic only applies when Monet is enabled.
+ if (!mIsMonetEnabled) {
+ return;
+ }
+
// Upon boot, make sure we have the most up to date colors
Runnable updateColors = () -> {
WallpaperColors systemColor = mWallpaperManager.getWallpaperColors(
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
index 2216a91..3be1d3c 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
@@ -38,6 +38,7 @@
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.pip.PipTransitionState;
import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.pip.tv.TvPipController;
import com.android.wm.shell.pip.tv.TvPipMenuController;
@@ -144,10 +145,17 @@
@WMSingleton
@Provides
+ static PipTransitionState providePipTransitionState() {
+ return new PipTransitionState();
+ }
+
+ @WMSingleton
+ @Provides
static PipTaskOrganizer providePipTaskOrganizer(Context context,
TvPipMenuController tvPipMenuController,
SyncTransactionQueue syncTransactionQueue,
PipBoundsState pipBoundsState,
+ PipTransitionState pipTransitionState,
PipBoundsAlgorithm pipBoundsAlgorithm,
PipAnimationController pipAnimationController,
PipTransitionController pipTransitionController,
@@ -157,7 +165,7 @@
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
return new PipTaskOrganizer(context,
- syncTransactionQueue, pipBoundsState, pipBoundsAlgorithm,
+ syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm,
tvPipMenuController, pipAnimationController, pipSurfaceTransactionHelper,
pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger,
shellTaskOrganizer, mainExecutor);
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 92ef850..131944f 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -61,6 +61,7 @@
import com.android.wm.shell.onehanded.OneHandedUiEventLogger;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.protolog.ShellProtoLogImpl;
+import com.android.wm.shell.splitscreen.SplitScreen;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -101,7 +102,8 @@
// Shell interfaces
private final Optional<Pip> mPipOptional;
- private final Optional<LegacySplitScreen> mSplitScreenOptional;
+ private final Optional<LegacySplitScreen> mLegacySplitScreenOptional;
+ private final Optional<SplitScreen> mSplitScreenOptional;
private final Optional<OneHanded> mOneHandedOptional;
private final Optional<HideDisplayCutout> mHideDisplayCutoutOptional;
private final Optional<ShellCommandHandler> mShellCommandHandler;
@@ -116,6 +118,7 @@
private final Executor mSysUiMainExecutor;
private boolean mIsSysUiStateValid;
+ private KeyguardUpdateMonitorCallback mLegacySplitScreenKeyguardCallback;
private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback;
private KeyguardUpdateMonitorCallback mPipKeyguardCallback;
private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback;
@@ -123,7 +126,8 @@
@Inject
public WMShell(Context context,
Optional<Pip> pipOptional,
- Optional<LegacySplitScreen> splitScreenOptional,
+ Optional<LegacySplitScreen> legacySplitScreenOptional,
+ Optional<SplitScreen> splitScreenOptional,
Optional<OneHanded> oneHandedOptional,
Optional<HideDisplayCutout> hideDisplayCutoutOptional,
Optional<ShellCommandHandler> shellCommandHandler,
@@ -143,6 +147,7 @@
mScreenLifecycle = screenLifecycle;
mSysUiState = sysUiState;
mPipOptional = pipOptional;
+ mLegacySplitScreenOptional = legacySplitScreenOptional;
mSplitScreenOptional = splitScreenOptional;
mOneHandedOptional = oneHandedOptional;
mHideDisplayCutoutOptional = hideDisplayCutoutOptional;
@@ -158,6 +163,7 @@
mProtoTracer.add(this);
mCommandQueue.addCallback(this);
mPipOptional.ifPresent(this::initPip);
+ mLegacySplitScreenOptional.ifPresent(this::initLegacySplitScreen);
mSplitScreenOptional.ifPresent(this::initSplitScreen);
mOneHandedOptional.ifPresent(this::initOneHanded);
mHideDisplayCutoutOptional.ifPresent(this::initHideDisplayCutout);
@@ -211,8 +217,8 @@
}
@VisibleForTesting
- void initSplitScreen(LegacySplitScreen legacySplitScreen) {
- mSplitScreenKeyguardCallback = new KeyguardUpdateMonitorCallback() {
+ void initLegacySplitScreen(LegacySplitScreen legacySplitScreen) {
+ mLegacySplitScreenKeyguardCallback = new KeyguardUpdateMonitorCallback() {
@Override
public void onKeyguardVisibilityChanged(boolean showing) {
// Hide the divider when keyguard is showing. Even though keyguard/statusbar is
@@ -222,6 +228,17 @@
legacySplitScreen.onKeyguardVisibilityChanged(showing);
}
};
+ mKeyguardUpdateMonitor.registerCallback(mLegacySplitScreenKeyguardCallback);
+ }
+
+ @VisibleForTesting
+ void initSplitScreen(SplitScreen splitScreen) {
+ mSplitScreenKeyguardCallback = new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onKeyguardOccludedChanged(boolean occluded) {
+ splitScreen.onKeyguardOccludedChanged(occluded);
+ }
+ };
mKeyguardUpdateMonitor.registerCallback(mSplitScreenKeyguardCallback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index 6ef7450..7e733a9 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -194,11 +194,12 @@
ShellTaskOrganizer organizer,
DisplayController displayController,
@ShellMainThread ShellExecutor mainExecutor,
- @ShellMainThread Handler mainHandler) {
+ @ShellMainThread Handler mainHandler,
+ SyncTransactionQueue syncQueue) {
return Optional.of(BubbleController.create(context, null /* synchronizer */,
floatingContentCoordinator, statusBarService, windowManager,
windowManagerShellWrapper, launcherApps, taskStackListener,
- uiEventLogger, organizer, displayController, mainExecutor, mainHandler));
+ uiEventLogger, organizer, displayController, mainExecutor, mainHandler, syncQueue));
}
//
@@ -424,8 +425,9 @@
@Provides
static TaskViewFactoryController provideTaskViewFactoryController(
ShellTaskOrganizer shellTaskOrganizer,
- @ShellMainThread ShellExecutor mainExecutor) {
- return new TaskViewFactoryController(shellTaskOrganizer, mainExecutor);
+ @ShellMainThread ShellExecutor mainExecutor,
+ SyncTransactionQueue syncQueue) {
+ return new TaskViewFactoryController(shellTaskOrganizer, mainExecutor, syncQueue);
}
//
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index 36fd9be..be7813e 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -48,6 +48,7 @@
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransition;
import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.pip.PipTransitionState;
import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.pip.phone.PhonePipMenuController;
import com.android.wm.shell.pip.phone.PipAppOpsListener;
@@ -184,8 +185,15 @@
@WMSingleton
@Provides
+ static PipTransitionState providePipTransitionState() {
+ return new PipTransitionState();
+ }
+
+ @WMSingleton
+ @Provides
static PipTaskOrganizer providePipTaskOrganizer(Context context,
SyncTransactionQueue syncTransactionQueue,
+ PipTransitionState pipTransitionState,
PipBoundsState pipBoundsState,
PipBoundsAlgorithm pipBoundsAlgorithm,
PhonePipMenuController menuPhoneController,
@@ -197,7 +205,7 @@
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
return new PipTaskOrganizer(context,
- syncTransactionQueue, pipBoundsState, pipBoundsAlgorithm,
+ syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm,
menuPhoneController, pipAnimationController, pipSurfaceTransactionHelper,
pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger,
shellTaskOrganizer, mainExecutor);
@@ -215,8 +223,9 @@
static PipTransitionController providePipTransitionController(Context context,
Transitions transitions, ShellTaskOrganizer shellTaskOrganizer,
PipAnimationController pipAnimationController, PipBoundsAlgorithm pipBoundsAlgorithm,
- PipBoundsState pipBoundsState, PhonePipMenuController pipMenuController) {
- return new PipTransition(context, pipBoundsState, pipMenuController,
+ PipBoundsState pipBoundsState, PipTransitionState pipTransitionState,
+ PhonePipMenuController pipMenuController) {
+ return new PipTransition(context, pipBoundsState, pipTransitionState, pipMenuController,
pipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer);
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index 10ed1d7..ce02b83 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -19,6 +19,9 @@
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
+import static com.android.keyguard.KeyguardClockSwitch.LARGE;
+import static com.android.keyguard.KeyguardClockSwitch.SMALL;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
@@ -247,4 +250,36 @@
verify(plugin).setStyle(style);
}
+
+ @Test
+ public void switchingToBigClock_makesSmallClockDisappear() {
+ mKeyguardClockSwitch.switchToClock(LARGE);
+
+ mKeyguardClockSwitch.mClockInAnim.end();
+ mKeyguardClockSwitch.mClockOutAnim.end();
+
+ assertThat(mLargeClockFrame.getAlpha()).isEqualTo(1);
+ assertThat(mLargeClockFrame.getVisibility()).isEqualTo(VISIBLE);
+ assertThat(mClockFrame.getAlpha()).isEqualTo(0);
+ }
+
+ @Test
+ public void switchingToSmallClock_makesBigClockDisappear() {
+ mKeyguardClockSwitch.switchToClock(SMALL);
+
+ mKeyguardClockSwitch.mClockInAnim.end();
+ mKeyguardClockSwitch.mClockOutAnim.end();
+
+ assertThat(mClockFrame.getAlpha()).isEqualTo(1);
+ assertThat(mClockFrame.getVisibility()).isEqualTo(VISIBLE);
+ // only big clock is removed at switch
+ assertThat(mLargeClockFrame.getParent()).isNull();
+ assertThat(mLargeClockFrame.getAlpha()).isEqualTo(0);
+ }
+
+ @Test
+ public void switchingToBigClock_returnsTrueOnlyWhenItWasNotVisibleBefore() {
+ assertThat(mKeyguardClockSwitch.switchToClock(LARGE)).isTrue();
+ assertThat(mKeyguardClockSwitch.switchToClock(LARGE)).isFalse();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index ca857c5..64bdc2e 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -19,11 +19,13 @@
import static android.view.WindowInsets.Type.ime;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -33,6 +35,7 @@
import android.content.res.Resources;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.view.MotionEvent;
import android.view.WindowInsetsController;
import androidx.test.filters.SmallTest;
@@ -43,6 +46,7 @@
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -58,6 +62,7 @@
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper()
public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
+ private static final int VIEW_WIDTH = 1600;
@Rule
public MockitoRule mRule = MockitoJUnit.rule();
@@ -100,6 +105,8 @@
private EmergencyButtonController mEmergencyButtonController;
@Mock
private Resources mResources;
+ @Mock
+ private FalsingCollector mFalsingCollector;
private Configuration mConfiguration;
private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
@@ -112,7 +119,9 @@
mConfiguration.setToDefaults(); // Defaults to ORIENTATION_UNDEFINED.
when(mResources.getConfiguration()).thenReturn(mConfiguration);
+ when(mView.getContext()).thenReturn(mContext);
when(mView.getResources()).thenReturn(mResources);
+ when(mView.getWidth()).thenReturn(VIEW_WIDTH);
when(mAdminSecondaryLockScreenControllerFactory.create(any(KeyguardSecurityCallback.class)))
.thenReturn(mAdminSecondaryLockScreenController);
when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController);
@@ -131,7 +140,7 @@
mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
mKeyguardStateController, mKeyguardSecurityViewFlipperController,
- mConfigurationController)
+ mConfigurationController, mFalsingCollector)
.create(mSecurityCallback);
}
@@ -169,18 +178,156 @@
public void onResourcesUpdate_callsThroughOnRotationChange() {
// Rotation is the same, shouldn't cause an update
mKeyguardSecurityContainerController.updateResources();
- verify(mView, times(0)).updateLayoutForSecurityMode(any());
+ verify(mView, times(0)).setOneHandedMode(anyBoolean());
// Update rotation. Should trigger update
mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE;
mKeyguardSecurityContainerController.updateResources();
- verify(mView, times(1)).updateLayoutForSecurityMode(any());
+ verify(mView, times(1)).setOneHandedMode(anyBoolean());
}
@Test
- public void updateKeyguardPosition_callsThroughToView() {
+ public void updateKeyguardPosition_callsThroughToViewInOneHandedMode() {
+ when(mView.isOneHandedMode()).thenReturn(true);
+ mKeyguardSecurityContainerController.updateKeyguardPosition(VIEW_WIDTH / 3f);
+ verify(mView).setOneHandedModeLeftAligned(true, false);
+
+ mKeyguardSecurityContainerController.updateKeyguardPosition((VIEW_WIDTH / 3f) * 2);
+ verify(mView).setOneHandedModeLeftAligned(false, false);
+ }
+
+ @Test
+ public void updateKeyguardPosition_ignoredInTwoHandedMode() {
+ when(mView.isOneHandedMode()).thenReturn(false);
mKeyguardSecurityContainerController.updateKeyguardPosition(1.0f);
- verify(mView).updateKeyguardPosition(1.0f);
+ verify(mView, never()).setOneHandedModeLeftAligned(anyBoolean(), anyBoolean());
+ }
+
+ private void touchDownLeftSide() {
+ mKeyguardSecurityContainerController.mGlobalTouchListener.onTouchEvent(
+ MotionEvent.obtain(
+ /* downTime= */0,
+ /* eventTime= */0,
+ MotionEvent.ACTION_DOWN,
+ /* x= */VIEW_WIDTH / 3f,
+ /* y= */0,
+ /* metaState= */0));
+ }
+
+ private void touchDownRightSide() {
+ mKeyguardSecurityContainerController.mGlobalTouchListener.onTouchEvent(
+ MotionEvent.obtain(
+ /* downTime= */0,
+ /* eventTime= */0,
+ MotionEvent.ACTION_DOWN,
+ /* x= */(VIEW_WIDTH / 3f) * 2,
+ /* y= */0,
+ /* metaState= */0));
+ }
+
+ @Test
+ public void onInterceptTap_inhibitsFalsingInOneHandedMode() {
+ when(mView.isOneHandedMode()).thenReturn(true);
+ when(mView.isOneHandedModeLeftAligned()).thenReturn(true);
+
+ touchDownLeftSide();
+ verify(mFalsingCollector, never()).avoidGesture();
+
+ // Now on the right.
+ touchDownRightSide();
+ verify(mFalsingCollector).avoidGesture();
+
+ // Move and re-test
+ reset(mFalsingCollector);
+ when(mView.isOneHandedModeLeftAligned()).thenReturn(false);
+
+ // On the right...
+ touchDownRightSide();
+ verify(mFalsingCollector, never()).avoidGesture();
+
+ touchDownLeftSide();
+ verify(mFalsingCollector).avoidGesture();
+ }
+
+ @Test
+ public void showSecurityScreen_oneHandedMode_bothFlagsDisabled_noOneHandedMode() {
+ setUpKeyguardFlags(
+ /* deviceConfigCanUseOneHandedKeyguard= */false,
+ /* sysuiResourceCanUseOneHandedKeyguard= */false);
+
+ when(mKeyguardSecurityViewFlipperController.getSecurityView(
+ eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class)))
+ .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
+
+ mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern);
+ verify(mView).setOneHandedMode(false);
+ }
+
+ @Test
+ public void showSecurityScreen_oneHandedMode_deviceFlagDisabled_noOneHandedMode() {
+ setUpKeyguardFlags(
+ /* deviceConfigCanUseOneHandedKeyguard= */false,
+ /* sysuiResourceCanUseOneHandedKeyguard= */true);
+
+ when(mKeyguardSecurityViewFlipperController.getSecurityView(
+ eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class)))
+ .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
+
+ mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern);
+ verify(mView).setOneHandedMode(false);
+ }
+
+ @Test
+ public void showSecurityScreen_oneHandedMode_sysUiFlagDisabled_noOneHandedMode() {
+ setUpKeyguardFlags(
+ /* deviceConfigCanUseOneHandedKeyguard= */true,
+ /* sysuiResourceCanUseOneHandedKeyguard= */false);
+
+ when(mKeyguardSecurityViewFlipperController.getSecurityView(
+ eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class)))
+ .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
+
+ mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern);
+ verify(mView).setOneHandedMode(false);
+ }
+
+ @Test
+ public void showSecurityScreen_oneHandedMode_bothFlagsEnabled_oneHandedMode() {
+ setUpKeyguardFlags(
+ /* deviceConfigCanUseOneHandedKeyguard= */true,
+ /* sysuiResourceCanUseOneHandedKeyguard= */true);
+
+ when(mKeyguardSecurityViewFlipperController.getSecurityView(
+ eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class)))
+ .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
+
+ mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern);
+ verify(mView).setOneHandedMode(true);
+ }
+
+ @Test
+ public void showSecurityScreen_twoHandedMode_bothFlagsEnabled_noOneHandedMode() {
+ setUpKeyguardFlags(
+ /* deviceConfigCanUseOneHandedKeyguard= */true,
+ /* sysuiResourceCanUseOneHandedKeyguard= */true);
+
+ when(mKeyguardSecurityViewFlipperController.getSecurityView(
+ eq(SecurityMode.Password), any(KeyguardSecurityCallback.class)))
+ .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
+
+ mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Password);
+ verify(mView).setOneHandedMode(false);
+ }
+
+ private void setUpKeyguardFlags(
+ boolean deviceConfigCanUseOneHandedKeyguard,
+ boolean sysuiResourceCanUseOneHandedKeyguard) {
+ when(mResources.getBoolean(
+ com.android.internal.R.bool.config_enableDynamicKeyguardPositioning))
+ .thenReturn(deviceConfigCanUseOneHandedKeyguard);
+ when(mResources.getBoolean(
+ R.bool.can_use_one_handed_bouncer))
+ .thenReturn(sysuiResourceCanUseOneHandedKeyguard);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index f5916e7..0276323 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -28,7 +28,6 @@
import android.graphics.Insets;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.testing.TestableResources;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
@@ -37,8 +36,6 @@
import androidx.test.filters.SmallTest;
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
@@ -57,11 +54,6 @@
private static final int FAKE_MEASURE_SPEC =
View.MeasureSpec.makeMeasureSpec(SCREEN_WIDTH, View.MeasureSpec.EXACTLY);
- private static final SecurityMode ONE_HANDED_SECURITY_MODE = SecurityMode.PIN;
- private static final SecurityMode TWO_HANDED_SECURITY_MODE = SecurityMode.Password;
-
-
-
@Rule
public MockitoRule mRule = MockitoJUnit.rule();
@@ -90,45 +82,8 @@
}
@Test
- public void onMeasure_usesFullWidthWithoutOneHandedMode() {
- setUpKeyguard(
- /* deviceConfigCanUseOneHandedKeyguard= */false,
- /* sysuiResourceCanUseOneHandedKeyguard= */ false,
- ONE_HANDED_SECURITY_MODE);
-
- mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
-
- verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
- }
-
- @Test
- public void onMeasure_usesFullWidthWithDeviceFlagDisabled() {
- setUpKeyguard(
- /* deviceConfigCanUseOneHandedKeyguard= */false,
- /* sysuiResourceCanUseOneHandedKeyguard= */ true,
- ONE_HANDED_SECURITY_MODE);
-
- mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
- verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
- }
-
- @Test
- public void onMeasure_usesFullWidthWithSysUIFlagDisabled() {
- setUpKeyguard(
- /* deviceConfigCanUseOneHandedKeyguard= */true,
- /* sysuiResourceCanUseOneHandedKeyguard= */ false,
- ONE_HANDED_SECURITY_MODE);
-
- mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
- verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
- }
-
- @Test
- public void onMeasure_usesHalfWidthWithFlagsEnabled() {
- setUpKeyguard(
- /* deviceConfigCanUseOneHandedKeyguard= */true,
- /* sysuiResourceCanUseOneHandedKeyguard= */ true,
- ONE_HANDED_SECURITY_MODE);
+ public void onMeasure_usesHalfWidthWithOneHandedModeEnabled() {
+ mKeyguardSecurityContainer.setOneHandedMode(/* oneHandedMode= */true);
int halfWidthMeasureSpec =
View.MeasureSpec.makeMeasureSpec(SCREEN_WIDTH / 2, View.MeasureSpec.EXACTLY);
@@ -138,11 +93,8 @@
}
@Test
- public void onMeasure_usesFullWidthForFullScreenIme() {
- setUpKeyguard(
- /* deviceConfigCanUseOneHandedKeyguard= */true,
- /* sysuiResourceCanUseOneHandedKeyguard= */ true,
- TWO_HANDED_SECURITY_MODE);
+ public void onMeasure_usesFullWidthWithOneHandedModeDisabled() {
+ mKeyguardSecurityContainer.setOneHandedMode(/* oneHandedMode= */false);
mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
@@ -153,10 +105,7 @@
int imeInsetAmount = 100;
int systemBarInsetAmount = 10;
- setUpKeyguard(
- /* deviceConfigCanUseOneHandedKeyguard= */false,
- /* sysuiResourceCanUseOneHandedKeyguard= */ false,
- ONE_HANDED_SECURITY_MODE);
+ mKeyguardSecurityContainer.setOneHandedMode(/* oneHandedMode= */false);
Insets imeInset = Insets.of(0, 0, 0, imeInsetAmount);
Insets systemBarInset = Insets.of(0, 0, 0, systemBarInsetAmount);
@@ -180,10 +129,7 @@
int imeInsetAmount = 0;
int systemBarInsetAmount = 10;
- setUpKeyguard(
- /* deviceConfigCanUseOneHandedKeyguard= */false,
- /* sysuiResourceCanUseOneHandedKeyguard= */ false,
- ONE_HANDED_SECURITY_MODE);
+ mKeyguardSecurityContainer.setOneHandedMode(/* oneHandedMode= */false);
Insets imeInset = Insets.of(0, 0, 0, imeInsetAmount);
Insets systemBarInset = Insets.of(0, 0, 0, systemBarInsetAmount);
@@ -201,56 +147,41 @@
verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, expectedHeightMeasureSpec);
}
- private void setupForUpdateKeyguardPosition(SecurityMode securityMode) {
- setUpKeyguard(
- /* deviceConfigCanUseOneHandedKeyguard= */true,
- /* sysuiResourceCanUseOneHandedKeyguard= */ true,
- securityMode);
+ private void setupForUpdateKeyguardPosition(boolean oneHandedMode) {
+ mKeyguardSecurityContainer.setOneHandedMode(oneHandedMode);
+ mKeyguardSecurityContainer.setOneHandedModeLeftAligned(true, false);
mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
mKeyguardSecurityContainer.layout(0, 0, SCREEN_WIDTH, SCREEN_WIDTH);
- // Start off left-aligned. This should happen anyway, but just do this to ensure
- // definitely move to the left.
- mKeyguardSecurityContainer.updateKeyguardPosition(0.0f);
-
// Clear any interactions with the mock so we know the interactions definitely come from the
// below testing.
reset(mSecurityViewFlipper);
}
@Test
- public void updateKeyguardPosition_movesKeyguard() {
- setupForUpdateKeyguardPosition(ONE_HANDED_SECURITY_MODE);
+ public void setIsLeftAligned_movesKeyguard() {
+ setupForUpdateKeyguardPosition(/* oneHandedMode= */ true);
- mKeyguardSecurityContainer.updateKeyguardPosition((SCREEN_WIDTH / 4f) * 3f);
+ mKeyguardSecurityContainer.setOneHandedModeLeftAligned(
+ /* leftAligned= */false, /* animate= */false);
verify(mSecurityViewFlipper).setTranslationX(SCREEN_WIDTH / 2.0f);
- mKeyguardSecurityContainer.updateKeyguardPosition(0.0f);
+ mKeyguardSecurityContainer.setOneHandedModeLeftAligned(
+ /* leftAligned= */true, /* animate= */false);
verify(mSecurityViewFlipper).setTranslationX(0.0f);
}
@Test
- public void updateKeyguardPosition_doesntMoveTwoHandedKeyguard() {
- setupForUpdateKeyguardPosition(TWO_HANDED_SECURITY_MODE);
+ public void setIsLeftAligned_doesntMoveTwoHandedKeyguard() {
+ setupForUpdateKeyguardPosition(/* oneHandedMode= */ false);
- mKeyguardSecurityContainer.updateKeyguardPosition((SCREEN_WIDTH / 4f) * 3f);
+ mKeyguardSecurityContainer.setOneHandedModeLeftAligned(
+ /* leftAligned= */false, /* animate= */false);
verify(mSecurityViewFlipper, never()).setTranslationX(anyInt());
- mKeyguardSecurityContainer.updateKeyguardPosition(0.0f);
+ mKeyguardSecurityContainer.setOneHandedModeLeftAligned(
+ /* leftAligned= */true, /* animate= */false);
verify(mSecurityViewFlipper, never()).setTranslationX(anyInt());
}
-
- private void setUpKeyguard(
- boolean deviceConfigCanUseOneHandedKeyguard,
- boolean sysuiResourceCanUseOneHandedKeyguard,
- SecurityMode securityMode) {
- TestableResources testableResources = mContext.getOrCreateTestableResources();
- testableResources.addOverride(
- com.android.internal.R.bool.config_enableDynamicKeyguardPositioning,
- deviceConfigCanUseOneHandedKeyguard);
- testableResources.addOverride(R.bool.can_use_one_handed_bouncer,
- sysuiResourceCanUseOneHandedKeyguard);
- mKeyguardSecurityContainer.updateLayoutForSecurityMode(securityMode);
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index 83e7b17..003368d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -103,7 +103,6 @@
@Mock private IWindowManager mWindowManager;
@Mock private Executor mBackgroundExecutor;
@Mock private UiEventLogger mUiEventLogger;
- @Mock private GlobalActionsInfoProvider mInfoProvider;
@Mock private RingerModeTracker mRingerModeTracker;
@Mock private RingerModeLiveData mRingerModeLiveData;
@Mock private SysUiState mSysUiState;
@@ -147,7 +146,6 @@
mWindowManager,
mBackgroundExecutor,
mUiEventLogger,
- mInfoProvider,
mRingerModeTracker,
mSysUiState,
mHandler
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsInfoProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsInfoProviderTest.kt
deleted file mode 100644
index 302a8d3..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsInfoProviderTest.kt
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.systemui.globalactions
-
-import android.content.Context
-import android.content.SharedPreferences
-import android.content.res.Configuration
-import android.content.res.Resources
-import android.service.quickaccesswallet.QuickAccessWalletClient
-import android.testing.AndroidTestingRunner
-import android.view.ViewGroup
-import androidx.test.filters.SmallTest
-import com.android.systemui.R
-import com.android.systemui.controls.controller.ControlsController
-import com.android.systemui.globalactions.GlobalActionsInfoProvider
-import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.SysuiTestCase
-import junit.framework.Assert.assertFalse
-import junit.framework.Assert.assertTrue
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.anyBoolean
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.ArgumentMatchers.anyObject
-import org.mockito.ArgumentMatchers.anyString
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.spy
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-import org.mockito.Mockito.`when` as whenever
-
-private const val PREFERENCE = "global_actions_info_prefs"
-private const val KEY_VIEW_COUNT = "view_count"
-
-private fun <T> eq(value: T): T = Mockito.eq(value) ?: value
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-class GlobalActionsInfoProviderTest : SysuiTestCase() {
-
- @Mock private lateinit var walletClient: QuickAccessWalletClient
- @Mock private lateinit var controlsController: ControlsController
- @Mock private lateinit var activityStarter: ActivityStarter
- @Mock private lateinit var mockContext: Context
- @Mock private lateinit var mockResources: Resources
- @Mock private lateinit var sharedPrefs: SharedPreferences
- @Mock private lateinit var sharedPrefsEditor: SharedPreferences.Editor
-
- private lateinit var infoProvider: GlobalActionsInfoProvider
-
- @Before
- fun setup() {
- MockitoAnnotations.initMocks(this)
- mockContext = spy(context)
- mockResources = spy(context.resources)
- whenever(mockContext.resources).thenReturn(mockResources)
- whenever(mockResources.getBoolean(R.bool.global_actions_show_change_info))
- .thenReturn(true)
- whenever(mockContext.getSharedPreferences(eq(PREFERENCE), anyInt()))
- .thenReturn(sharedPrefs)
- whenever(sharedPrefs.edit()).thenReturn(sharedPrefsEditor)
- whenever(sharedPrefsEditor.putInt(anyString(), anyInt())).thenReturn(sharedPrefsEditor)
- whenever(sharedPrefsEditor.putBoolean(anyString(), anyBoolean()))
- .thenReturn(sharedPrefsEditor)
-
- infoProvider = GlobalActionsInfoProvider(
- mockContext,
- walletClient,
- controlsController,
- activityStarter
- )
- }
-
- @Test
- fun testIsEligible_noCards() {
- whenever(sharedPrefs.contains(eq(KEY_VIEW_COUNT))).thenReturn(false)
- whenever(walletClient.isWalletFeatureAvailable).thenReturn(false)
-
- assertFalse(infoProvider.shouldShowMessage())
- }
-
- @Test
- fun testIsEligible_hasCards() {
- whenever(sharedPrefs.contains(eq(KEY_VIEW_COUNT))).thenReturn(false)
- whenever(walletClient.isWalletFeatureAvailable).thenReturn(true)
-
- assertTrue(infoProvider.shouldShowMessage())
- }
-
- @Test
- fun testNotEligible_shouldNotShow() {
- whenever(mockResources.getBoolean(R.bool.global_actions_show_change_info))
- .thenReturn(false)
-
- assertFalse(infoProvider.shouldShowMessage())
- }
-
- @Test
- fun testTooManyButtons_doesNotAdd() {
- val configuration = Configuration()
- configuration.orientation = Configuration.ORIENTATION_LANDSCAPE
- whenever(mockResources.configuration).thenReturn(configuration)
-
- val parent = mock(ViewGroup::class.java)
- infoProvider.addPanel(mockContext, parent, 5, { })
-
- verify(parent, never()).addView(anyObject(), anyInt())
- }
-
- @Test
- fun testLimitTimesShown() {
- whenever(sharedPrefs.getInt(eq(KEY_VIEW_COUNT), anyInt())).thenReturn(4)
-
- assertFalse(infoProvider.shouldShowMessage())
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
index da63b8a..3b5431f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
@@ -108,7 +108,8 @@
Dependency.get(Dependency.MAIN_HANDLER),
mock(UiEventLogger.class),
mock(NavigationBarOverlayController.class),
- mock(ConfigurationController.class)));
+ mock(ConfigurationController.class),
+ mock(NavigationBarA11yHelper.class)));
initializeNavigationBars();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java
index eac68f6..b991976 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java
@@ -31,9 +31,6 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.SysuiTestableContext;
-import com.android.systemui.navigationbar.buttons.KeyButtonDrawable;
-import com.android.systemui.navigationbar.RotationButton;
-import com.android.systemui.navigationbar.RotationButtonController;
import com.android.systemui.statusbar.policy.RotationLockController;
import org.junit.Before;
@@ -46,7 +43,6 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class NavigationBarRotationContextTest extends SysuiTestCase {
- static final int RES_UNDEF = 0;
static final int DEFAULT_ROTATE = 0;
@Rule
@@ -66,7 +62,6 @@
mRotationButtonController.setRotationButton(mRotationButton, (visibility) -> {});
// Due to a mockito issue, only spy the object after setting the initial state
mRotationButtonController = spy(mRotationButtonController);
- final KeyButtonDrawable kbd = mock(KeyButtonDrawable.class);
doReturn(view).when(mRotationButton).getCurrentView();
doReturn(true).when(mRotationButton).acceptRotationProposal();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 4ec45b4..6359ca8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -276,7 +276,8 @@
mock(SystemActions.class),
mHandler,
mock(NavigationBarOverlayController.class),
- mUiEventLogger));
+ mUiEventLogger,
+ mock(NavigationBarA11yHelper.class)));
}
private void processAllMessages() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
similarity index 91%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index f376e88..6ee2f20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -92,7 +92,7 @@
*/
@SmallTest
@RunWith(AndroidTestingRunner.class)
-public class NotificationStackScrollerControllerTest extends SysuiTestCase {
+public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
@Mock private NotificationGutsManager mNotificationGutsManager;
@Mock private HeadsUpManagerPhone mHeadsUpManager;
@@ -232,16 +232,15 @@
reset(mNotificationStackScrollLayout);
mController.updateShowEmptyShadeView();
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
- true /* visible */,
-
- true /* notifVisibleInShade */);
+ /* visible= */ true,
+ /* notifVisibleInShade= */ true);
setupShowEmptyShadeViewState(stateListener, false);
reset(mNotificationStackScrollLayout);
mController.updateShowEmptyShadeView();
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
- false /* visible */,
- true /* notifVisibleInShade */);
+ /* visible= */ false,
+ /* notifVisibleInShade= */ true);
}
@Test
@@ -257,15 +256,42 @@
reset(mNotificationStackScrollLayout);
mController.updateShowEmptyShadeView();
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
- true /* visible */,
- false /* notifVisibleInShade */);
+ /* visible= */ true,
+ /* notifVisibleInShade= */ false);
setupShowEmptyShadeViewState(stateListener, false);
reset(mNotificationStackScrollLayout);
mController.updateShowEmptyShadeView();
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
- false /* visible */,
- false /* notifVisibleInShade */);
+ /* visible= */ false,
+ /* notifVisibleInShade= */ false);
+ }
+
+ @Test
+ public void testUpdateEmptyShadeView_splitShadeMode_alwaysShowEmptyView() {
+ when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
+ mController.attach(mNotificationStackScrollLayout);
+ verify(mSysuiStatusBarStateController).addCallback(
+ mStateListenerArgumentCaptor.capture(), anyInt());
+ StatusBarStateController.StateListener stateListener =
+ mStateListenerArgumentCaptor.getValue();
+ when(mNotificationStackScrollLayout.isUsingSplitNotificationShade()).thenReturn(true);
+ stateListener.onStateChanged(SHADE);
+ mController.getView().removeAllViews();
+
+ mController.setQsExpanded(false);
+ reset(mNotificationStackScrollLayout);
+ mController.updateShowEmptyShadeView();
+ verify(mNotificationStackScrollLayout).updateEmptyShadeView(
+ /* visible= */ true,
+ /* notifVisibleInShade= */ false);
+
+ mController.setQsExpanded(true);
+ reset(mNotificationStackScrollLayout);
+ mController.updateShowEmptyShadeView();
+ verify(mNotificationStackScrollLayout).updateEmptyShadeView(
+ /* visible= */ true,
+ /* notifVisibleInShade= */ false);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index c6e5697..41046c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -18,6 +18,8 @@
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static com.android.keyguard.KeyguardClockSwitch.LARGE;
+import static com.android.keyguard.KeyguardClockSwitch.SMALL;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
@@ -32,6 +34,7 @@
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -686,6 +689,38 @@
.setNotificationScrimTop(NOTIFICATION_SCRIM_TOP_PADDING_IN_SPLIT_SHADE);
}
+ @Test
+ public void testSwitchesToCorrectClockInSinglePaneShade() {
+ mStatusBarStateController.setState(KEYGUARD);
+
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+ triggerPositionClockAndNotifications();
+ verify(mKeyguardStatusViewController).displayClock(LARGE);
+
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
+ mNotificationPanelViewController.closeQs();
+ verify(mKeyguardStatusViewController).displayClock(SMALL);
+ }
+
+ @Test
+ public void testSwitchesToCorrectClockInSplitShade() {
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade();
+
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+ triggerPositionClockAndNotifications();
+ verify(mKeyguardStatusViewController).displayClock(LARGE);
+
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
+ triggerPositionClockAndNotifications();
+ verify(mKeyguardStatusViewController, times(2)).displayClock(LARGE);
+ verify(mKeyguardStatusViewController, never()).displayClock(SMALL);
+ }
+
+ private void triggerPositionClockAndNotifications() {
+ mNotificationPanelViewController.closeQs();
+ }
+
private FalsingManager.FalsingTapListener getFalsingTapListener() {
for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) {
listener.onViewAttachedToWindow(mView);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index e55361e..30059a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -556,6 +556,49 @@
}
@Test
+ public void disableClipQsScrimWithoutStateTransition_updatesTintAndAlpha() {
+ mScrimController.setClipsQsScrim(true);
+ mScrimController.transitionTo(ScrimState.BOUNCER);
+
+ mScrimController.setClipsQsScrim(false);
+
+ finishAnimationsImmediately();
+ // Front scrim should be transparent
+ // Back scrim should be visible without tint
+ assertScrimAlpha(Map.of(
+ mScrimInFront, TRANSPARENT,
+ mNotificationsScrim, TRANSPARENT,
+ mScrimBehind, OPAQUE));
+ assertScrimTinted(Map.of(
+ mScrimInFront, false,
+ mScrimBehind, false,
+ mNotificationsScrim, false
+ ));
+ }
+
+ @Test
+ public void enableClipQsScrimWithoutStateTransition_updatesTintAndAlpha() {
+ mScrimController.setClipsQsScrim(false);
+ mScrimController.transitionTo(ScrimState.BOUNCER);
+
+ mScrimController.setClipsQsScrim(true);
+
+ finishAnimationsImmediately();
+ // Front scrim should be transparent
+ // Back scrim should be clipping QS
+ // Notif scrim should be visible without tint
+ assertScrimAlpha(Map.of(
+ mScrimInFront, TRANSPARENT,
+ mNotificationsScrim, OPAQUE,
+ mScrimBehind, OPAQUE));
+ assertScrimTinted(Map.of(
+ mScrimInFront, false,
+ mScrimBehind, true,
+ mNotificationsScrim, false
+ ));
+ }
+
+ @Test
public void transitionToBouncer() {
mScrimController.transitionTo(ScrimState.BOUNCER_SCRIMMED);
finishAnimationsImmediately();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index dd8354d..0d4d889 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -14,6 +14,8 @@
package com.android.systemui.statusbar.policy;
+import static android.view.ContentInfo.SOURCE_CLIPBOARD;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
@@ -25,15 +27,19 @@
import android.app.ActivityManager;
import android.app.PendingIntent;
import android.app.RemoteInput;
+import android.content.ClipData;
+import android.content.ClipDescription;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ShortcutManager;
+import android.net.Uri;
import android.os.Handler;
import android.os.Process;
import android.os.UserHandle;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.view.ContentInfo;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
@@ -238,4 +244,39 @@
RemoteInputView.NotificationRemoteInputEvent.NOTIFICATION_REMOTE_INPUT_SEND.getId(),
mUiEventLoggerFake.eventId(1));
}
+
+ @Test
+ public void testUiEventLogging_openAndAttach() throws Exception {
+ NotificationTestHelper helper = new NotificationTestHelper(
+ mContext,
+ mDependency,
+ TestableLooper.get(this));
+ ExpandableNotificationRow row = helper.createRow();
+ RemoteInputView view = RemoteInputView.inflate(mContext, null, row.getEntry(), mController);
+
+ setTestPendingIntent(view);
+
+ // Open view, attach an image
+ view.focus();
+ EditText editText = view.findViewById(R.id.remote_input_text);
+ editText.setText(TEST_REPLY);
+ ClipDescription description = new ClipDescription("", new String[] {"image/png"});
+ // We need to use an (arbitrary) real resource here so that an actual image gets attached.
+ ClipData clip = new ClipData(description, new ClipData.Item(
+ Uri.parse("android.resource://com.android.systemui/"
+ + R.drawable.default_thumbnail)));
+ ContentInfo payload =
+ new ContentInfo.Builder(clip, SOURCE_CLIPBOARD).build();
+ view.setAttachment(payload);
+ mReceiver.waitForIntent();
+
+ assertEquals(2, mUiEventLoggerFake.numLogs());
+ assertEquals(
+ RemoteInputView.NotificationRemoteInputEvent.NOTIFICATION_REMOTE_INPUT_OPEN.getId(),
+ mUiEventLoggerFake.eventId(0));
+ assertEquals(
+ RemoteInputView.NotificationRemoteInputEvent
+ .NOTIFICATION_REMOTE_INPUT_ATTACH_IMAGE.getId(),
+ mUiEventLoggerFake.eventId(1));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
new file mode 100644
index 0000000..c915bcb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.policy
+
+import android.app.IActivityTaskManager
+import android.content.DialogInterface
+import android.content.Intent
+import android.content.pm.UserInfo
+import android.graphics.Bitmap
+import android.os.Handler
+import android.os.UserManager
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.internal.util.UserIcons
+import com.android.systemui.GuestResumeSessionReceiver
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.qs.QSUserSwitcherEvent
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.telephony.TelephonyListenerManager
+import com.android.systemui.util.settings.SecureSettings
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotNull
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyString
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class UserSwitcherControllerTest : SysuiTestCase() {
+ @Mock private lateinit var keyguardStateController: KeyguardStateController
+ @Mock private lateinit var handler: Handler
+ @Mock private lateinit var userTracker: UserTracker
+ @Mock private lateinit var userManager: UserManager
+ @Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
+ @Mock private lateinit var activityTaskManager: IActivityTaskManager
+ @Mock private lateinit var userDetailAdapter: UserSwitcherController.UserDetailAdapter
+ @Mock private lateinit var telephonyListenerManager: TelephonyListenerManager
+ @Mock private lateinit var userInfo: UserInfo
+ @Mock private lateinit var secureSettings: SecureSettings
+ private lateinit var testableLooper: TestableLooper
+ private lateinit var userSwitcherController: UserSwitcherController
+ private lateinit var uiEventLogger: UiEventLoggerFake
+ private lateinit var picture: Bitmap
+ private val guestId = 1234
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ testableLooper = TestableLooper.get(this)
+ uiEventLogger = UiEventLoggerFake()
+
+ userSwitcherController = UserSwitcherController(context,
+ userManager,
+ userTracker,
+ keyguardStateController,
+ handler,
+ activityStarter,
+ broadcastDispatcher,
+ uiEventLogger,
+ telephonyListenerManager,
+ activityTaskManager,
+ userDetailAdapter,
+ secureSettings)
+ picture = UserIcons.convertToBitmap(context.getDrawable(R.drawable.ic_avatar_user))
+ }
+
+ @Test
+ fun testAddGuest_okButtonPressed_isLogged() {
+ val emptyGuestUserRecord = UserSwitcherController.UserRecord(
+ null,
+ null,
+ true /* guest */,
+ false /* current */,
+ false /* isAddUser */,
+ false /* isRestricted */,
+ true /* isSwitchToEnabled */)
+
+ `when`(userManager.createGuest(any(), anyString())).thenReturn(userInfo)
+
+ userSwitcherController.onUserListItemClicked(emptyGuestUserRecord)
+ assertEquals(1, uiEventLogger.numLogs())
+ assertEquals(QSUserSwitcherEvent.QS_USER_GUEST_ADD.id, uiEventLogger.eventId(0))
+ }
+
+ @Test
+ fun testRemoveGuest_removeButtonPressed_isLogged() {
+ val currentGuestUserRecord = UserSwitcherController.UserRecord(
+ userInfo,
+ picture,
+ true /* guest */,
+ true /* current */,
+ false /* isAddUser */,
+ false /* isRestricted */,
+ true /* isSwitchToEnabled */)
+
+ userSwitcherController.onUserListItemClicked(currentGuestUserRecord)
+ assertNotNull(userSwitcherController.mExitGuestDialog)
+ userSwitcherController.mExitGuestDialog
+ .getButton(DialogInterface.BUTTON_POSITIVE).performClick()
+ testableLooper.processAllMessages()
+ assertEquals(1, uiEventLogger.numLogs())
+ assertEquals(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE.id, uiEventLogger.eventId(0))
+ }
+
+ @Test
+ fun testRemoveGuest_cancelButtonPressed_isNotLogged() {
+ val currentGuestUserRecord = UserSwitcherController.UserRecord(
+ userInfo,
+ picture,
+ true /* guest */,
+ true /* current */,
+ false /* isAddUser */,
+ false /* isRestricted */,
+ true /* isSwitchToEnabled */)
+
+ userSwitcherController.onUserListItemClicked(currentGuestUserRecord)
+ assertNotNull(userSwitcherController.mExitGuestDialog)
+ userSwitcherController.mExitGuestDialog
+ .getButton(DialogInterface.BUTTON_NEGATIVE).performClick()
+ testableLooper.processAllMessages()
+ assertEquals(0, uiEventLogger.numLogs())
+ }
+
+ @Test
+ fun testWipeGuest_startOverButtonPressed_isLogged() {
+ val guestInfo = UserInfo(guestId, null, null, 0, UserManager.USER_TYPE_FULL_GUEST)
+ val currentGuestUserRecord = UserSwitcherController.UserRecord(
+ guestInfo,
+ picture,
+ true /* guest */,
+ false /* current */,
+ false /* isAddUser */,
+ false /* isRestricted */,
+ true /* isSwitchToEnabled */)
+
+ // Simulate that guest user has already logged in
+ `when`(secureSettings.getIntForUser(
+ eq(GuestResumeSessionReceiver.SETTING_GUEST_HAS_LOGGED_IN), anyInt(), anyInt()))
+ .thenReturn(1)
+
+ userSwitcherController.onUserListItemClicked(currentGuestUserRecord)
+
+ // Simulate a user switch event
+ val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, guestId)
+ `when`(userTracker.userInfo).thenReturn(guestInfo)
+
+ assertNotNull(userSwitcherController.mGuestResumeSessionReceiver)
+ userSwitcherController.mGuestResumeSessionReceiver.onReceive(context, intent)
+
+ assertNotNull(userSwitcherController.mGuestResumeSessionReceiver.mNewSessionDialog)
+ userSwitcherController.mGuestResumeSessionReceiver.mNewSessionDialog
+ .getButton(GuestResumeSessionReceiver.ResetSessionDialog.BUTTON_WIPE).performClick()
+ testableLooper.processAllMessages()
+ assertEquals(1, uiEventLogger.numLogs())
+ assertEquals(QSUserSwitcherEvent.QS_USER_GUEST_WIPE.id, uiEventLogger.eventId(0))
+ }
+
+ @Test
+ fun testWipeGuest_continueButtonPressed_isLogged() {
+ val guestInfo = UserInfo(guestId, null, null, 0, UserManager.USER_TYPE_FULL_GUEST)
+ val currentGuestUserRecord = UserSwitcherController.UserRecord(
+ guestInfo,
+ picture,
+ true /* guest */,
+ false /* current */,
+ false /* isAddUser */,
+ false /* isRestricted */,
+ true /* isSwitchToEnabled */)
+
+ // Simulate that guest user has already logged in
+ `when`(secureSettings.getIntForUser(
+ eq(GuestResumeSessionReceiver.SETTING_GUEST_HAS_LOGGED_IN), anyInt(), anyInt()))
+ .thenReturn(1)
+
+ userSwitcherController.onUserListItemClicked(currentGuestUserRecord)
+
+ // Simulate a user switch event
+ val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, guestId)
+ `when`(userTracker.userInfo).thenReturn(guestInfo)
+
+ assertNotNull(userSwitcherController.mGuestResumeSessionReceiver)
+ userSwitcherController.mGuestResumeSessionReceiver.onReceive(context, intent)
+
+ assertNotNull(userSwitcherController.mGuestResumeSessionReceiver.mNewSessionDialog)
+ userSwitcherController.mGuestResumeSessionReceiver.mNewSessionDialog
+ .getButton(GuestResumeSessionReceiver.ResetSessionDialog.BUTTON_DONTWIPE)
+ .performClick()
+ testableLooper.processAllMessages()
+ assertEquals(1, uiEventLogger.numLogs())
+ assertEquals(QSUserSwitcherEvent.QS_USER_GUEST_CONTINUE.id, uiEventLogger.eventId(0))
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 6e2e4cb..30918de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -115,6 +115,7 @@
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.google.common.collect.ImmutableList;
@@ -318,7 +319,8 @@
mPositioner,
mock(DisplayController.class),
syncExecutor,
- mock(Handler.class));
+ mock(Handler.class),
+ mock(SyncTransactionQueue.class));
mBubbleController.setExpandListener(mBubbleExpandListener);
mBubblesManager = new BubblesManager(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
index 9339f81..9b9756d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
@@ -95,6 +95,7 @@
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerImpl;
import org.junit.Before;
@@ -262,7 +263,8 @@
mPositioner,
mock(DisplayController.class),
syncExecutor,
- mock(Handler.class));
+ mock(Handler.class),
+ mock(SyncTransactionQueue.class));
mBubbleController.setExpandListener(mBubbleExpandListener);
mBubblesManager = new BubblesManager(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
index cd5aa9a..7b77cb0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
@@ -32,6 +32,7 @@
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerImpl;
/**
@@ -54,11 +55,12 @@
BubblePositioner positioner,
DisplayController displayController,
ShellExecutor shellMainExecutor,
- Handler shellMainHandler) {
+ Handler shellMainHandler,
+ SyncTransactionQueue syncQueue) {
super(context, data, Runnable::run, floatingContentCoordinator, dataRepository,
statusBarService, windowManager, windowManagerShellWrapper, launcherApps,
bubbleLogger, taskStackListener, shellTaskOrganizer, positioner, displayController,
- shellMainExecutor, shellMainHandler);
+ shellMainExecutor, shellMainHandler, syncQueue);
setInflateSynchronously(true);
initialize();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index 1dd0b21..300907c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -39,6 +39,7 @@
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.splitscreen.SplitScreen;
import org.junit.Before;
import org.junit.Test;
@@ -67,6 +68,7 @@
@Mock SysUiState mSysUiState;
@Mock Pip mPip;
@Mock LegacySplitScreen mLegacySplitScreen;
+ @Mock SplitScreen mSplitScreen;
@Mock OneHanded mOneHanded;
@Mock HideDisplayCutout mHideDisplayCutout;
@Mock ProtoTracer mProtoTracer;
@@ -78,7 +80,7 @@
MockitoAnnotations.initMocks(this);
mWMShell = new WMShell(mContext, Optional.of(mPip), Optional.of(mLegacySplitScreen),
- Optional.of(mOneHanded), Optional.of(mHideDisplayCutout),
+ Optional.of(mSplitScreen), Optional.of(mOneHanded), Optional.of(mHideDisplayCutout),
Optional.of(mShellCommandHandler), mCommandQueue, mConfigurationController,
mKeyguardUpdateMonitor, mNavigationModeController,
mScreenLifecycle, mSysUiState, mProtoTracer, mSysUiMainExecutor);
@@ -92,8 +94,15 @@
}
@Test
+ public void initLegacySplitScreen_registersCallbacks() {
+ mWMShell.initLegacySplitScreen(mLegacySplitScreen);
+
+ verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class));
+ }
+
+ @Test
public void initSplitScreen_registersCallbacks() {
- mWMShell.initSplitScreen(mLegacySplitScreen);
+ mWMShell.initSplitScreen(mSplitScreen);
verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class));
}
diff --git a/packages/VpnDialogs/res/values-af/strings.xml b/packages/VpnDialogs/res/values-af/strings.xml
index ac82b0e..88ccbd9 100644
--- a/packages/VpnDialogs/res/values-af/strings.xml
+++ b/packages/VpnDialogs/res/values-af/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Verbindingversoek"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wil \'n VPN-verbinding opstel wat dit sal toelaat om netwerkverkeer te monitor. Aanvaar dit net as jy die bron vertrou. <br /> <br /> <img src=vpn_icon /> verskyn boaan jou skerm as VPN aktief is."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> wil \'n VPN-verbinding opstel wat dit toelaat om netwerkverkeer te monitor. Aanvaar dit net as jy die bron vertrou. <br /> <br /> <img src=vpn_icon /> verskyn op jou skerm wanneer VPN aktief is."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN is gekoppel"</string>
<string name="session" msgid="6470628549473641030">"Sessie:"</string>
<string name="duration" msgid="3584782459928719435">"Tydsduur:"</string>
diff --git a/packages/VpnDialogs/res/values-am/strings.xml b/packages/VpnDialogs/res/values-am/strings.xml
index ad9773b..9fc5ff4 100644
--- a/packages/VpnDialogs/res/values-am/strings.xml
+++ b/packages/VpnDialogs/res/values-am/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"የግንኙነት ጥያቄ"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> የአውታረ መረብ መከታተል የሚያስችል የVPN ግንኑነት ማዋቀር ይፈልጋል። ምንጩን የሚያምኑት ብቻ ከሆኑ ይቀበሉ። <br /> <br /> <img src=vpn_icon /> VPN ገቢር ሲሆን በማያ ገጽዎ ላይኛው ክፍል ላይ ይታያል።"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> የአውታረ መረብ ትራፊክን ለመቆጣጠር የሚያስችል የVPN ግንኙነትን ማዋቀር ይፈልጋል። ምንጩን የሚያምኑ ከሆነ ብቻ ይቀበሉ። <br /> <br /> <img src=vpn_icon /> ማያ ገጹ ላይ VPN ገቢር ሲሆን ይታያል።"</string>
<string name="legacy_title" msgid="192936250066580964">"VPN ተያይዟል"</string>
<string name="session" msgid="6470628549473641030">"ክፍለ ጊዜ፡"</string>
<string name="duration" msgid="3584782459928719435">"ጊዜ"</string>
diff --git a/packages/VpnDialogs/res/values-ar/strings.xml b/packages/VpnDialogs/res/values-ar/strings.xml
index 808cde9..33be6a3 100644
--- a/packages/VpnDialogs/res/values-ar/strings.xml
+++ b/packages/VpnDialogs/res/values-ar/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"طلب الاتصال"</string>
<string name="warning" msgid="809658604548412033">"يريد <xliff:g id="APP">%s</xliff:g> إعداد الاتصال بالشبكة الافتراضية الخاصة التي تتيح له مراقبة حركة المرور على الشبكة. فلا توافق إلا إذا كنت تثق في المصدر. <br /> <br /> <img src=vpn_icon /> يظهر في الجزء العلوي من الشاشة عندما تكون الشبكة الافتراضية الخاصة نشطة."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"يريد تطبيق <xliff:g id="APP">%s</xliff:g> إعداد اتصال شبكة افتراضية خاصة (VPN) يتيح له مراقبة حركة بيانات الشبكة. لا تقبل السماح بذلك إلا إذا كنت تثق في المصدر. <br /> <br /> <img src=vpn_icon /> يظهر على شاشتك عندما تكون الشبكة الافتراضية الخاصة نشطة."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN متصلة"</string>
<string name="session" msgid="6470628549473641030">"الجلسة"</string>
<string name="duration" msgid="3584782459928719435">"المدة:"</string>
diff --git a/packages/VpnDialogs/res/values-as/strings.xml b/packages/VpnDialogs/res/values-as/strings.xml
index 45d8458..4669a69 100644
--- a/packages/VpnDialogs/res/values-as/strings.xml
+++ b/packages/VpnDialogs/res/values-as/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"সংযোগৰ অনুৰোধ"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g>এ নেটৱৰ্ক ট্ৰেফিক নিৰীক্ষণ কৰিবলৈ এটা ভিপিএন সংযোগ ছেট আপ কৰিবলৈ বিচাৰিছে৷ আপুনি কেৱল উৎসটোক বিশ্বাস কৰিলেহে অনুৰোধ স্বীকাৰ কৰিব৷ ভিপিএন সক্ৰিয় থকাৰ সময়ত আপোনাৰ স্ক্ৰীণৰ ওপৰত <br /> <br /> <img src=vpn_icon /> দৃশ্যমান হয়৷"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g>এ এটা ভিপিএন সংযোগ ছেট আপ কৰিব বিচাৰে, যিটোৱে ইয়াক নেটৱৰ্ক ট্ৰেফিক নিৰীক্ষণ কৰিবলৈ দিয়ে। আপুনি উৎসটোক বিশ্বাস কৰিলেহে গ্ৰহণ কৰক। ভিপিএনটো সক্ৰিয় হৈ থকাৰ সময়ত আপোনাৰ স্ক্ৰীনত<br /> <br /> <img src=vpn_icon /> প্ৰদৰ্শিত হয়।"</string>
<string name="legacy_title" msgid="192936250066580964">"ভিপিএন সংযোগ হৈ আছে"</string>
<string name="session" msgid="6470628549473641030">"ছেশ্বন:"</string>
<string name="duration" msgid="3584782459928719435">"সময়সীমা:"</string>
diff --git a/packages/VpnDialogs/res/values-az/strings.xml b/packages/VpnDialogs/res/values-az/strings.xml
index 2bdf23e..d878835 100644
--- a/packages/VpnDialogs/res/values-az/strings.xml
+++ b/packages/VpnDialogs/res/values-az/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Bağlantı Sorğusu"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> VPN bağlantı yaratmaq istəyir ki, bu da şəbəkə trafikini izləyə bilər. Yalnız mənbəyə güvəndiyiniz halda qəbul edin. VPN aktiv olan zaman <br /> <br /> <img src=vpn_icon /> ekranın yuxarısında görünür."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> şəbəkə trafikini izləməyə imkan verən VPN bağlantısı yaratmaq istəyir. Yalnız mənbəyə güvəndiyiniz halda qəbul edin. <br /> <br /> <img src=vpn_icon /> VPN aktiv olan zaman ekranda görünür."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN qoşuludur"</string>
<string name="session" msgid="6470628549473641030">"Sessiya:"</string>
<string name="duration" msgid="3584782459928719435">"Müddət:"</string>
diff --git a/packages/VpnDialogs/res/values-b+sr+Latn/strings.xml b/packages/VpnDialogs/res/values-b+sr+Latn/strings.xml
index f40e406..a1075d2 100644
--- a/packages/VpnDialogs/res/values-b+sr+Latn/strings.xml
+++ b/packages/VpnDialogs/res/values-b+sr+Latn/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Zahtev za povezivanje"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> želi da podesi VPN vezu koja omogućava praćenje saobraćaja na mreži. Prihvatite samo ako verujete izvoru. <br /> <br /> <img src=vpn_icon /> se prikazuje u vrhu ekrana kada je VPN aktivan."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Aplikacija <xliff:g id="APP">%s</xliff:g> želi da podesi VPN vezu koja joj omogućava da prati mrežni saobraćaj. Prihvatite ovo samo ako imate poverenja u izvor. <br /> <br /> <img src=vpn_icon /> se prikazuje na ekranu kada je VPN aktivan."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN je povezan"</string>
<string name="session" msgid="6470628549473641030">"Sesija:"</string>
<string name="duration" msgid="3584782459928719435">"Trajanje:"</string>
diff --git a/packages/VpnDialogs/res/values-be/strings.xml b/packages/VpnDialogs/res/values-be/strings.xml
index 0903c8e..fc3f878 100644
--- a/packages/VpnDialogs/res/values-be/strings.xml
+++ b/packages/VpnDialogs/res/values-be/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Запыт на падлучэнне"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> спрабуе наладзіць падлучэнне VPN, якое дазваляе сачыць за сеткавым трафікам. Прымайце толькі тады, калі вы давяраеце гэтай крыніцы. Калі VPN актыўны, у верхняй частцы экрана адлюстроўваецца <br /> <br /> <img src=vpn_icon />."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Праграма \"<xliff:g id="APP">%s</xliff:g>\" запытвае дазвол на падключэнне да сеткі VPN, каб адсочваць сеткавы трафік. Дайце дазвол, толькі калі вы давяраеце крыніцы. Калі адбудзецца падключэнне да VPN, на экране з\'явіцца значок <br /> <br /> <img src=vpn_icon />."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN падключаны"</string>
<string name="session" msgid="6470628549473641030">"Сессія"</string>
<string name="duration" msgid="3584782459928719435">"Працягласць:"</string>
diff --git a/packages/VpnDialogs/res/values-bg/strings.xml b/packages/VpnDialogs/res/values-bg/strings.xml
index 9ac853d..6345f1d 100644
--- a/packages/VpnDialogs/res/values-bg/strings.xml
+++ b/packages/VpnDialogs/res/values-bg/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Заявка за свързване"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> иска да настрои връзка с виртуална частна мрежа (VPN), за да може да наблюдава мрежовия трафик. Приемете само ако източникът е надежден. Иконата <br /> <br /> <img src=vpn_icon /> се показва в долната част на екрана при активирана VPN."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> иска да настрои връзка с VPN, за да може да наблюдава трафика в мрежата. Приемете само ако източникът е надежден. <br /> <br /> <img src=vpn_icon /> се показва на екрана при активирана VPN."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN е свързана"</string>
<string name="session" msgid="6470628549473641030">"Сесия:"</string>
<string name="duration" msgid="3584782459928719435">"Продължителност:"</string>
diff --git a/packages/VpnDialogs/res/values-bn/strings.xml b/packages/VpnDialogs/res/values-bn/strings.xml
index 5e11fd9..352b786 100644
--- a/packages/VpnDialogs/res/values-bn/strings.xml
+++ b/packages/VpnDialogs/res/values-bn/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"সংযোগের অনুরোধ"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> এমন একটি VPN সংযোগ সেট-আপ করতে চাচ্ছে যেটি দিয়ে এটি নেটওয়ার্ক ট্রাফিক নিরীক্ষণ করতে পারবে। আপনি যদি উৎসটিকে বিশ্বাস করেন, তাহলেই কেবল এতে সম্মতি দিন। VPN সক্রিয় থাকলে আপনার স্ক্রীনের উপরে <br /> <br /> <img src=vpn_icon /> দেখা যাবে।"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> এমন একটি VPN সংযোগ সেট আপ করতে চাইছে যেটি দিয়ে এটি নেটওয়ার্ক ট্রাফিক নিরীক্ষণ করতে পারবে। আপনি সোর্সটি বিশ্বাস করলে একমাত্র তখনই অ্যাক্সেপ্ট করুন। PN অ্যাক্টিভ থাকলে <br /> <br /> <img src=vpn_icon /> আপনার স্ক্রিনে দেখা যায়।"</string>
<string name="legacy_title" msgid="192936250066580964">"VPN সংযুক্ত হয়েছে"</string>
<string name="session" msgid="6470628549473641030">"অধিবেশন:"</string>
<string name="duration" msgid="3584782459928719435">"সময়কাল:"</string>
diff --git a/packages/VpnDialogs/res/values-bs/strings.xml b/packages/VpnDialogs/res/values-bs/strings.xml
index 56812d5..fa5f4ea 100644
--- a/packages/VpnDialogs/res/values-bs/strings.xml
+++ b/packages/VpnDialogs/res/values-bs/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Zahtjev za povezivanje"</string>
<string name="warning" msgid="809658604548412033">"Aplikacija <xliff:g id="APP">%s</xliff:g> želi podesiti VPN vezu koja joj omogućava praćenje mrežnog saobraćaja. Prihvatite samo ako je izvor pouzdan. <br /> <br /> <img src=vpn_icon /> se pojavi na vrhu ekrana kada je VPN aktivna."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Aplikacija <xliff:g id="APP">%s</xliff:g> želi postaviti VPN vezu koja joj omogućava praćenje mrežnog saobraćaja. Prihvatite samo ako vjerujete izvoru. Kada je VPN aktivan, na ekranu se prikazuje ikona <br /> <br /> <img src=vpn_icon />."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN veza uspostavljena"</string>
<string name="session" msgid="6470628549473641030">"Sesija:"</string>
<string name="duration" msgid="3584782459928719435">"Trajanje:"</string>
diff --git a/packages/VpnDialogs/res/values-ca/strings.xml b/packages/VpnDialogs/res/values-ca/strings.xml
index 97738c3..cdb7547 100644
--- a/packages/VpnDialogs/res/values-ca/strings.xml
+++ b/packages/VpnDialogs/res/values-ca/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Sol·licitud de connexió"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> vol configurar una connexió VPN que li permeti controlar el trànsit de xarxa. Accepta la sol·licitud només si prové d\'una font de confiança. <br /> <br /> <img src=vpn_icon /> es mostra a la part superior de la pantalla quan la VPN està activada."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> vol configurar una connexió VPN que li permeti monitorar el trànsit de xarxa. Accepta la sol·licitud només si prové d\'una font de confiança. <br /> <br /> <img src=vpn_icon /> és la icona que veuràs a la pantalla quan la VPN estigui activa."</string>
<string name="legacy_title" msgid="192936250066580964">"La VPN està connectada"</string>
<string name="session" msgid="6470628549473641030">"Sessió:"</string>
<string name="duration" msgid="3584782459928719435">"Durada:"</string>
diff --git a/packages/VpnDialogs/res/values-cs/strings.xml b/packages/VpnDialogs/res/values-cs/strings.xml
index 5cc809c..c06f6ff 100644
--- a/packages/VpnDialogs/res/values-cs/strings.xml
+++ b/packages/VpnDialogs/res/values-cs/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Žádost o připojení"</string>
<string name="warning" msgid="809658604548412033">"Aplikace <xliff:g id="APP">%s</xliff:g> žádá o nastavení připojení VPN, pomocí kterého bude moci sledovat síťový provoz. Povolte, jen pokud zdroji důvěřujete. <br /> <br /> <img src=vpn_icon /> – když je síť VPN aktivní, v horní části obrazovky se zobrazuje tato ikona."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Aplikace <xliff:g id="APP">%s</xliff:g> chce nastavit připojení VPN, které umožňuje sledovat síťový provoz. Povolte, jen pokud zdroji důvěřujete. <br /> <br /> <img src=vpn_icon /> – když je síť VPN aktivní, na obrazovce se zobrazuje tato ikona."</string>
<string name="legacy_title" msgid="192936250066580964">"Síť VPN je připojena"</string>
<string name="session" msgid="6470628549473641030">"Relace:"</string>
<string name="duration" msgid="3584782459928719435">"Doba trvání:"</string>
diff --git a/packages/VpnDialogs/res/values-da/strings.xml b/packages/VpnDialogs/res/values-da/strings.xml
index 7641158..a4ddc19 100644
--- a/packages/VpnDialogs/res/values-da/strings.xml
+++ b/packages/VpnDialogs/res/values-da/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Forbindelsesanmodning"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> vil konfigurere en VPN-forbindelse, der giver appen mulighed for at registrere netværkstrafik. Du bør kun acceptere dette, hvis du har tillid til kilden. <br /> <br /> <img src=vpn_icon /> vises øverst på din skærm, når VPN-forbindelsen er aktiv."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> anmoder om at konfigurere en VPN-forbindelse, der giver appen tilladelse til at holde øje med netværkstrafik. Du bør kun acceptere dette, hvis du har tillid til appen. <br /> <br /> <img src=vpn_icon /> vises på din skærm, når VPN-forbindelsen er aktiv."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN er tilsluttet"</string>
<string name="session" msgid="6470628549473641030">"Session:"</string>
<string name="duration" msgid="3584782459928719435">"Varighed:"</string>
diff --git a/packages/VpnDialogs/res/values-de/strings.xml b/packages/VpnDialogs/res/values-de/strings.xml
index 0f1e009..f38e395 100644
--- a/packages/VpnDialogs/res/values-de/strings.xml
+++ b/packages/VpnDialogs/res/values-de/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Verbindungsanfrage"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> möchte eine VPN-Verbindung herstellen, über die der Netzwerkverkehr überwacht werden kann. Lass die Verbindung nur zu, wenn die App vertrauenswürdig ist. Wenn VPN aktiv ist, wird oben im Display <br /> <br /> <img src=vpn_icon /> angezeigt."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> möchte eine VPN-Verbindung herstellen, über die der Netzwerkverkehr überwacht werden kann. Lass die Verbindung nur zu, wenn die App vertrauenswürdig ist. <br /> <br /> <img src=vpn_icon /> wird auf dem Display angezeigt, wenn VPN aktiv ist."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN ist verbunden"</string>
<string name="session" msgid="6470628549473641030">"Sitzung:"</string>
<string name="duration" msgid="3584782459928719435">"Dauer:"</string>
diff --git a/packages/VpnDialogs/res/values-el/strings.xml b/packages/VpnDialogs/res/values-el/strings.xml
index 78bcc43..e3eb460 100644
--- a/packages/VpnDialogs/res/values-el/strings.xml
+++ b/packages/VpnDialogs/res/values-el/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Αίτημα σύνδεσης"</string>
<string name="warning" msgid="809658604548412033">"Η εφαρμογή <xliff:g id="APP">%s</xliff:g> επιθυμεί να ρυθμίσει μια σύνδεση VPN που της επιτρέπει να παρακολουθεί την επισκεψιμότητα του δικτύου. Αποδεχτείτε το αίτημα μόνο εάν εμπιστεύεστε την πηγή. Το εικονίδιο <br /> <br /> <img src=vpn_icon /> εμφανίζεται στο επάνω μέρος της οθόνης σας όταν είναι ενεργό το VPN."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Η εφαρμογή <xliff:g id="APP">%s</xliff:g> επιθυμεί να ρυθμίσει μια σύνδεση VPN που της επιτρέπει να παρακολουθεί την επισκεψιμότητα του δικτύου. Αποδεχτείτε το αίτημα μόνο εάν εμπιστεύεστε την πηγή. Το εικονίδιο <br /> <br /> <img src=vpn_icon /> εμφανίζεται στην οθόνη σας όταν είναι ενεργό το VPN."</string>
<string name="legacy_title" msgid="192936250066580964">"Το VPN συνδέθηκε"</string>
<string name="session" msgid="6470628549473641030">"Περίοδος σύνδεσης"</string>
<string name="duration" msgid="3584782459928719435">"Διάρκεια:"</string>
diff --git a/packages/VpnDialogs/res/values-en-rAU/strings.xml b/packages/VpnDialogs/res/values-en-rAU/strings.xml
index 6ed50a7..cb8b79d 100644
--- a/packages/VpnDialogs/res/values-en-rAU/strings.xml
+++ b/packages/VpnDialogs/res/values-en-rAU/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Connection request"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. <br /> <br /> <img src=vpn_icon /> appears at the top of your screen when VPN is active."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. <br /> <br /> <img src=vpn_icon /> appears on your screen when VPN is active."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN is connected"</string>
<string name="session" msgid="6470628549473641030">"Session:"</string>
<string name="duration" msgid="3584782459928719435">"Duration:"</string>
diff --git a/packages/VpnDialogs/res/values-en-rCA/strings.xml b/packages/VpnDialogs/res/values-en-rCA/strings.xml
index 6ed50a7..cb8b79d 100644
--- a/packages/VpnDialogs/res/values-en-rCA/strings.xml
+++ b/packages/VpnDialogs/res/values-en-rCA/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Connection request"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. <br /> <br /> <img src=vpn_icon /> appears at the top of your screen when VPN is active."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. <br /> <br /> <img src=vpn_icon /> appears on your screen when VPN is active."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN is connected"</string>
<string name="session" msgid="6470628549473641030">"Session:"</string>
<string name="duration" msgid="3584782459928719435">"Duration:"</string>
diff --git a/packages/VpnDialogs/res/values-en-rGB/strings.xml b/packages/VpnDialogs/res/values-en-rGB/strings.xml
index 6ed50a7..cb8b79d 100644
--- a/packages/VpnDialogs/res/values-en-rGB/strings.xml
+++ b/packages/VpnDialogs/res/values-en-rGB/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Connection request"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. <br /> <br /> <img src=vpn_icon /> appears at the top of your screen when VPN is active."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. <br /> <br /> <img src=vpn_icon /> appears on your screen when VPN is active."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN is connected"</string>
<string name="session" msgid="6470628549473641030">"Session:"</string>
<string name="duration" msgid="3584782459928719435">"Duration:"</string>
diff --git a/packages/VpnDialogs/res/values-en-rIN/strings.xml b/packages/VpnDialogs/res/values-en-rIN/strings.xml
index 6ed50a7..cb8b79d 100644
--- a/packages/VpnDialogs/res/values-en-rIN/strings.xml
+++ b/packages/VpnDialogs/res/values-en-rIN/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Connection request"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. <br /> <br /> <img src=vpn_icon /> appears at the top of your screen when VPN is active."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. <br /> <br /> <img src=vpn_icon /> appears on your screen when VPN is active."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN is connected"</string>
<string name="session" msgid="6470628549473641030">"Session:"</string>
<string name="duration" msgid="3584782459928719435">"Duration:"</string>
diff --git a/packages/VpnDialogs/res/values-en-rXC/strings.xml b/packages/VpnDialogs/res/values-en-rXC/strings.xml
index 9d010e6..f5e2deb 100644
--- a/packages/VpnDialogs/res/values-en-rXC/strings.xml
+++ b/packages/VpnDialogs/res/values-en-rXC/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Connection request"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. <br /> <br /> <img src=vpn_icon /> appears at the top of your screen when VPN is active."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. <br /> <br /> <img src=vpn_icon /> appears on your screen when VPN is active."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN is connected"</string>
<string name="session" msgid="6470628549473641030">"Session:"</string>
<string name="duration" msgid="3584782459928719435">"Duration:"</string>
diff --git a/packages/VpnDialogs/res/values-es-rUS/strings.xml b/packages/VpnDialogs/res/values-es-rUS/strings.xml
index 21cfc04..108a24e 100644
--- a/packages/VpnDialogs/res/values-es-rUS/strings.xml
+++ b/packages/VpnDialogs/res/values-es-rUS/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<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 capaz de controlar el tráfico de la red. Acéptala solo si confías en la fuente. <br /> <br /> <img src=vpn_icon /> aparece en la parte superior de la pantalla cuando se activa la 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 supervisar el tráfico de red. Solo acéptala si confías en la fuente. <br /> <br /> <img src=vpn_icon /> aparecerá en tu pantalla cuando se active la VPN."</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>
diff --git a/packages/VpnDialogs/res/values-es/strings.xml b/packages/VpnDialogs/res/values-es/strings.xml
index 372147f..0eaf359 100644
--- a/packages/VpnDialogs/res/values-es/strings.xml
+++ b/packages/VpnDialogs/res/values-es/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<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="session" msgid="6470628549473641030">"Sesión:"</string>
<string name="duration" msgid="3584782459928719435">"Duración:"</string>
diff --git a/packages/VpnDialogs/res/values-et/strings.xml b/packages/VpnDialogs/res/values-et/strings.xml
index c328cd7..140c183 100644
--- a/packages/VpnDialogs/res/values-et/strings.xml
+++ b/packages/VpnDialogs/res/values-et/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Ühendamise taotlus"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> tahab seadistada VPN-i ühenduse, mis võimaldab jälgida võrguliiklust. Nõustuge ainult siis, kui usaldate seda allikat. <br /> <br /> <img src=vpn_icon /> kuvatakse ekraani ülaservas, kui VPN on aktiivne."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> tahab seadistada VPN-i ühenduse, mis võimaldab jälgida võrguliiklust. Nõustuge ainult siis, kui usaldate seda allikat. <br /> <br /> <img src=vpn_icon /> kuvatakse ekraanil, kui VPN on aktiivne."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN on ühendatud"</string>
<string name="session" msgid="6470628549473641030">"Seansid"</string>
<string name="duration" msgid="3584782459928719435">"Kestus:"</string>
diff --git a/packages/VpnDialogs/res/values-eu/strings.xml b/packages/VpnDialogs/res/values-eu/strings.xml
index a3b7716e..9fc16e2 100644
--- a/packages/VpnDialogs/res/values-eu/strings.xml
+++ b/packages/VpnDialogs/res/values-eu/strings.xml
@@ -18,13 +18,14 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Konektatzeko eskaera"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> aplikazioak VPN bidezko konexioa ezarri nahi du sareko trafikoa kontrolatzeko. Iturburua fidagarria bada bakarrik baimendu. <br /> <br /> VPN konexioa aktibo dagoenean, <img src=vpn_icon /> agertuko da pantailaren goialdean."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> aplikazioak VPN bidezko konexio bat konfiguratu nahi du sareko trafikoa gainbegiratzeko. Onartu soilik iturburuaz fidatzen bazara. <br /> <br /> <img src=vpn_icon /> agertzen da pantailan, VPNa aktibo dagoenean."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN sarera konektatuta dago"</string>
<string name="session" msgid="6470628549473641030">"Saioa:"</string>
<string name="duration" msgid="3584782459928719435">"Iraupena:"</string>
<string name="data_transmitted" msgid="7988167672982199061">"Bidalita:"</string>
<string name="data_received" msgid="4062776929376067820">"Jasota:"</string>
<string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_0">%1$s</xliff:g> byte / <xliff:g id="NUMBER_1">%2$s</xliff:g> pakete"</string>
- <string name="always_on_disconnected_title" msgid="1906740176262776166">"Ezin da konektatu beti aktibatuta dagoen VPN sarea"</string>
+ <string name="always_on_disconnected_title" msgid="1906740176262776166">"Ezin da konektatu beti aktibatuta dagoen VPNa"</string>
<string name="always_on_disconnected_message" msgid="555634519845992917">"Beti aktibatuta egoteko dago konfiguratuta <xliff:g id="VPN_APP_0">%1$s</xliff:g>, baina une honetan ezin da konektatu. <xliff:g id="VPN_APP_1">%1$s</xliff:g> sarera berriro konektatu ahal izan arte, sare publiko bat erabiliko du telefonoak."</string>
<string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"Beti aktibatuta egoteko dago konfiguratuta <xliff:g id="VPN_APP">%1$s</xliff:g>, baina une honetan ezin da konektatu. VPN sarearen konexioa berreskuratu arte, ez duzu izango konexiorik."</string>
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
diff --git a/packages/VpnDialogs/res/values-fa/strings.xml b/packages/VpnDialogs/res/values-fa/strings.xml
index 56f847c..6fb5a00 100644
--- a/packages/VpnDialogs/res/values-fa/strings.xml
+++ b/packages/VpnDialogs/res/values-fa/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"درخواست اتصال"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> میخواهد یک اتصال VPN راهاندازی کند که به آن امکان نظارت بر ترافیک شبکه را میدهد. فقط در صورتی بپذیرید که به منبع آن اطمینان دارید. هنگامی که VPN فعال شد، <br /> <br /> <img src=vpn_icon /> در بالای صفحه نمایش شما نشان داده میشود."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> میخواهد یک اتصال VPN راهاندازی کند که به آن امکان نظارت بر ترافیک شبکه را میدهد. فقط درصورتیکه به منبع اعتماد دارید قبول کنید. وقتی VPN فعال باشد، <br /> <br /> <img src=vpn_icon /> در صفحهنمایش نشان داده میشود."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN متصل است"</string>
<string name="session" msgid="6470628549473641030">"جلسه:"</string>
<string name="duration" msgid="3584782459928719435">"مدت زمان:"</string>
diff --git a/packages/VpnDialogs/res/values-fi/strings.xml b/packages/VpnDialogs/res/values-fi/strings.xml
index 91c918a..8abca06 100644
--- a/packages/VpnDialogs/res/values-fi/strings.xml
+++ b/packages/VpnDialogs/res/values-fi/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Yhteyspyyntö"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> haluaa tehdä asetukset VPN-yhteydellä, jonka kautta sovellus voi valvoa verkkoliikennettä. Hyväksy vain, jos lähde on luotettava. <br /> <br /> <img src=vpn_icon /> näkyy ruudun yläreunassa, kun VPN on käytössä."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> haluaa muodostaa VPN-yhteyden, jonka avulla se voi valvoa verkkoliikennettä. Salli tämä vain, jos luotat lähteeseen. <br /> <br /> <img src=vpn_icon /> näkyy näytölläsi, kun VPN on aktiivinen."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN on yhdistetty"</string>
<string name="session" msgid="6470628549473641030">"Käyttökerta"</string>
<string name="duration" msgid="3584782459928719435">"Kesto:"</string>
diff --git a/packages/VpnDialogs/res/values-fr-rCA/strings.xml b/packages/VpnDialogs/res/values-fr-rCA/strings.xml
index aa86c7c..876111c 100644
--- a/packages/VpnDialogs/res/values-fr-rCA/strings.xml
+++ b/packages/VpnDialogs/res/values-fr-rCA/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Demande de connexion"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> veut configurer une connexion RPV qui permet de surveiller le trafic réseau. N\'acceptez que si vous faites confiance à la source. <br /><br /><img src=vpn_icon/> s\'affiche dans le haut de votre écran lorsqu\'une connexion RPV est active."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> veut configurer une connexion RPV qui permet de surveiller le trafic réseau. N\'acceptez que si vous faites confiance à la source. <br /> <br /> <img src=vpn_icon /> s\'affiche dans le haut de votre écran lorsqu\'une connexion RPV est active."</string>
<string name="legacy_title" msgid="192936250066580964">"RPV connecté"</string>
<string name="session" msgid="6470628549473641030">"Session :"</string>
<string name="duration" msgid="3584782459928719435">"Durée :"</string>
diff --git a/packages/VpnDialogs/res/values-fr/strings.xml b/packages/VpnDialogs/res/values-fr/strings.xml
index 7180119..27ebfb0 100644
--- a/packages/VpnDialogs/res/values-fr/strings.xml
+++ b/packages/VpnDialogs/res/values-fr/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Demande de connexion"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> souhaite configurer une connexion VPN qui lui permet de surveiller le trafic réseau. N\'acceptez que si vous faites confiance à la source. <br /> <br /> <img src=vpn_icon /> s\'affiche en haut de votre écran lorsqu\'une connexion VPN est active."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> souhaite configurer une connexion VPN qui lui permet de surveiller le trafic réseau. N\'acceptez que si vous faites confiance à la source. <br /> <br /> <img src=vpn_icon /> s\'affiche à l\'écran lorsqu\'un VPN est actif."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN connecté"</string>
<string name="session" msgid="6470628549473641030">"Session :"</string>
<string name="duration" msgid="3584782459928719435">"Durée :"</string>
diff --git a/packages/VpnDialogs/res/values-gl/strings.xml b/packages/VpnDialogs/res/values-gl/strings.xml
index 8a66d08..cd8ee8d 100644
--- a/packages/VpnDialogs/res/values-gl/strings.xml
+++ b/packages/VpnDialogs/res/values-gl/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Solicitude de conexión"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> quere configurar unha conexión VPN que lle permite controlar o tráfico da rede. Acepta soamente se confías na fonte. <br /> <br /> <img src=vpn_icon /> aparece na parte superior da pantalla cando se activa a VPN."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"A aplicación <xliff:g id="APP">%s</xliff:g> quere configurar unha conexión VPN que lle permita supervisar o tráfico de rede. Acepta só se confías nela. <br /> <br /> <img src=vpn_icon /> aparece na pantalla cando a VPN está activa."</string>
<string name="legacy_title" msgid="192936250066580964">"A VPN está conectada"</string>
<string name="session" msgid="6470628549473641030">"Sesión:"</string>
<string name="duration" msgid="3584782459928719435">"Duración:"</string>
diff --git a/packages/VpnDialogs/res/values-gu/strings.xml b/packages/VpnDialogs/res/values-gu/strings.xml
index 961711c..b5a8831 100644
--- a/packages/VpnDialogs/res/values-gu/strings.xml
+++ b/packages/VpnDialogs/res/values-gu/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"કનેક્શન વિનંતી"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> VPN કનેક્શન સેટ કરવા માગે છે જે તેને નેટવર્ક ટ્રાફિક મૉનિટર કરવાની મંજૂરી આપે છે. જો તમને સ્રોત પર વિશ્વાસ હોય તો જ સ્વીકારો. <br /> <br /> <img src=vpn_icon /> તમારી સ્ક્રીનની ટોચ પર ત્યારે દેખાય છે જ્યારે VPN સક્રિય હોય છે."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> એક એવું VPN કનેક્શન સેટ કરવા માગે છે કે જે તેને નેટવર્ક ટ્રાફિકનું નિરીક્ષણ કરવાની મંજૂરી આપતું હોય. જો તમને સૉર્સ પર વિશ્વાસ હોય તો જ સ્વીકારો. <br /> <br /> <img src=vpn_icon /> તમારી સ્ક્રીન પર ત્યારે દેખાય છે, જ્યારે VPN સક્રિય હોય છે."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN કનેક્ટ કરેલું છે"</string>
<string name="session" msgid="6470628549473641030">"સત્ર:"</string>
<string name="duration" msgid="3584782459928719435">"અવધિ:"</string>
diff --git a/packages/VpnDialogs/res/values-hi/strings.xml b/packages/VpnDialogs/res/values-hi/strings.xml
index eed0858..c9c65d5 100644
--- a/packages/VpnDialogs/res/values-hi/strings.xml
+++ b/packages/VpnDialogs/res/values-hi/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"कनेक्शन अनुरोध"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> वीपीएन कनेक्शन सेट अप करना चाहता है, जिससे वह नेटवर्क ट्रैफ़िक पर नज़र रख पाएगा. इसकी मंज़ूरी तभी दें जब आपको इस पर भरोसा हो. वीपीएन चालू होने पर <br /> <br /> <img src=vpn_icon /> आपकी स्क्रीन के सबसे ऊपर दिखाई देता है."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> को वीपीएन कनेक्शन सेट अप करने की अनुमति चाहिए. इससे वह नेटवर्क ट्रैफ़िक पर नज़र रख पाएगा. अनुमति तब दें, जब आपको ऐप्लिकेशन पर भरोसा हो. वीपीएन चालू होने पर, आपकी स्क्रीन पर <br /> <br /> <img src=vpn_icon /> दिखेगा."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN कनेक्ट है"</string>
<string name="session" msgid="6470628549473641030">"सत्र:"</string>
<string name="duration" msgid="3584782459928719435">"अवधि:"</string>
diff --git a/packages/VpnDialogs/res/values-hr/strings.xml b/packages/VpnDialogs/res/values-hr/strings.xml
index aa9e436..576d997 100644
--- a/packages/VpnDialogs/res/values-hr/strings.xml
+++ b/packages/VpnDialogs/res/values-hr/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Zahtjev za povezivanje"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> želi postaviti VPN vezu pomoću koje će moći nadzirati mrežni promet. Prihvatite samo ako smatrate izvor pouzdanim. Kada je VPN aktivan, pri vrhu zaslona prikazuje se <br /> <br /> <img src=vpn_icon />."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Aplikacija <xliff:g id="APP">%s</xliff:g> želi postaviti VPN vezu pomoću koje će moći nadzirati mrežni promet. Prihvatite samo ako smatrate izvor pouzdanim. Kad je VPN aktivan, na zaslonu se prikazuje ikona <br /> <br /> <img src=vpn_icon />."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN je spojen"</string>
<string name="session" msgid="6470628549473641030">"Sesija"</string>
<string name="duration" msgid="3584782459928719435">"Trajanje:"</string>
diff --git a/packages/VpnDialogs/res/values-hu/strings.xml b/packages/VpnDialogs/res/values-hu/strings.xml
index 703aa79..69b999f 100644
--- a/packages/VpnDialogs/res/values-hu/strings.xml
+++ b/packages/VpnDialogs/res/values-hu/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Kapcsolódási kérés"</string>
<string name="warning" msgid="809658604548412033">"A(z) <xliff:g id="APP">%s</xliff:g> VPN kapcsolatot akar beállítani, amelynek segítségével figyelheti a hálózati forgalmat. Csak akkor fogadja el, ha megbízik a forrásban. <br /> <br /> Amikor a VPN aktív, <img src=vpn_icon /> ikon jelenik meg a képernyő tetején."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"A(z) <xliff:g id="APP">%s</xliff:g> alkalmazás VPN-kapcsolatot szeretne beállítani, amely segítségével figyelheti a hálózati forgalmat. Csak akkor fogadja el, ha megbízik a forrásban. <br /> <br /> Amikor aktív a VPN, a következő ikon látható a képernyőn: <img src=vpn_icon />."</string>
<string name="legacy_title" msgid="192936250066580964">"A VPN csatlakoztatva van"</string>
<string name="session" msgid="6470628549473641030">"Munkamenet:"</string>
<string name="duration" msgid="3584782459928719435">"Időtartam:"</string>
diff --git a/packages/VpnDialogs/res/values-hy/strings.xml b/packages/VpnDialogs/res/values-hy/strings.xml
index c296c85..d2a6d42 100644
--- a/packages/VpnDialogs/res/values-hy/strings.xml
+++ b/packages/VpnDialogs/res/values-hy/strings.xml
@@ -17,7 +17,8 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Միացման հայց"</string>
- <string name="warning" msgid="809658604548412033">"«<xliff:g id="APP">%s</xliff:g>» հավելվածը ցանկանում է VPN կապ հաստատել՝ ցանցային երթևեկը հսկելու համար: Թույլատրեք, միայն եթե վստահում եք աղբյուրին: Երբ VPN-ն ակտիվ լինի, ձեր էկրանի վերին հատվածում կհայտնվի <br /> <br /> <img src=vpn_icon /> պատկերը:"</string>
+ <string name="warning" msgid="809658604548412033">"«<xliff:g id="APP">%s</xliff:g>» հավելվածը ցանկանում է VPN կապ հաստատել՝ ցանցային երթևեկը հսկելու համար: Թույլատրեք, միայն եթե վստահում եք աղբյուրին։ Երբ VPN-ն ակտիվ լինի, ձեր էկրանի վերին հատվածում կհայտնվի <br /> <br /> <img src=vpn_icon /> պատկերը:"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> հավելվածն ուզում է միանալ VPN-ի ցանցին՝ թրաֆիկին հետևելու համար։ Թույլատրեք, միայն եթե վստահում եք աղբյուրին։ Երբ VPN-ն ակտիվացված լինի, <br /> <br /> <img src=vpn_icon /> պատկերակը կհայտնվի ձեր էկրանին։"</string>
<string name="legacy_title" msgid="192936250066580964">"VPN-ը կապակցված է"</string>
<string name="session" msgid="6470628549473641030">"Աշխատաշրջան`"</string>
<string name="duration" msgid="3584782459928719435">"Տևողությունը՝"</string>
@@ -25,7 +26,7 @@
<string name="data_received" msgid="4062776929376067820">"Ստացվել է՝"</string>
<string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_0">%1$s</xliff:g> բայթ / <xliff:g id="NUMBER_1">%2$s</xliff:g> փաթեթ"</string>
<string name="always_on_disconnected_title" msgid="1906740176262776166">"Չի հաջողվում միանալ միշտ միացված VPN-ին"</string>
- <string name="always_on_disconnected_message" msgid="555634519845992917">"<xliff:g id="VPN_APP_0">%1$s</xliff:g>-ն այնպես է կարգավորված, որ միշտ միացած մնա, սակայն ներկայումս կապակցման խնդիր կա: Ձեր հեռախոսը կօգտագործի հանրային ցանցը, մինչև նորից կարողանա միանալ <xliff:g id="VPN_APP_1">%1$s</xliff:g>-ին:"</string>
+ <string name="always_on_disconnected_message" msgid="555634519845992917">"<xliff:g id="VPN_APP_0">%1$s</xliff:g>-ն այնպես է կարգավորված, որ միշտ միացած մնա, սակայն ներկայումս կապակցման խնդիր կա: Ձեր հեռախոսը կօգտագործի հանրային ցանցը, մինչև նորից կարողանա միանալ <xliff:g id="VPN_APP_1">%1$s</xliff:g>-ին։"</string>
<string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"<xliff:g id="VPN_APP">%1$s</xliff:g>-ն այնպես է կարգավորված, որ միշտ միացած մնա, սակայն ներկայումս կապակցման խնդիր կա: Մինչև VPN-ը նորից չմիանա, դուք կապ չեք ունենա:"</string>
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
<string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"Փոխել VPN-ի կարգավորումները"</string>
diff --git a/packages/VpnDialogs/res/values-in/strings.xml b/packages/VpnDialogs/res/values-in/strings.xml
index 18ef372a..059008f 100644
--- a/packages/VpnDialogs/res/values-in/strings.xml
+++ b/packages/VpnDialogs/res/values-in/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Permintaan sambungan"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ingin menyiapkan sambungan VPN yang memungkinkannya memantau traffic jaringan. Terima hanya jika Anda memercayai sumber. <br /> <br /> <img src=vpn_icon /> muncul di bagian atas layar Anda saat VPN aktif."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ingin menyiapkan koneksi VPN yang memungkinkannya memantau traffic jaringan. Hanya terima jika Anda memercayai sumbernya. <br /> <br /> <img src=vpn_icon /> muncul di layar bila VPN aktif."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN tersambung"</string>
<string name="session" msgid="6470628549473641030">"Sesi:"</string>
<string name="duration" msgid="3584782459928719435">"Durasi:"</string>
diff --git a/packages/VpnDialogs/res/values-is/strings.xml b/packages/VpnDialogs/res/values-is/strings.xml
index 70fb40f..a75371d 100644
--- a/packages/VpnDialogs/res/values-is/strings.xml
+++ b/packages/VpnDialogs/res/values-is/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Beiðni um tengingu"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> vill setja upp VPN-tengingu til þess að geta fylgst með netumferð. Samþykktu þetta aðeins ef þú treystir upprunanum. <br /> <br /> <img src=vpn_icon /> birtist efst á skjánum þegar VPN er virkt."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> vill setja upp VPN-tengingu til að fylgjast með netumferð. Ekki samþykkja þú treystir upprunanum. <br /> <br /> <img src=vpn_icon /> birtist á skjánum hjá þér þegar VPN er virkt."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN er tengt"</string>
<string name="session" msgid="6470628549473641030">"Lota:"</string>
<string name="duration" msgid="3584782459928719435">"Tímalengd:"</string>
diff --git a/packages/VpnDialogs/res/values-it/strings.xml b/packages/VpnDialogs/res/values-it/strings.xml
index 2602493..c443c51 100644
--- a/packages/VpnDialogs/res/values-it/strings.xml
+++ b/packages/VpnDialogs/res/values-it/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Richiesta di connessione"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> vuole impostare una connessione VPN che le consenta di monitorare il traffico di rete. Accetta soltanto se ritieni la fonte attendibile. Quando la connessione VPN è attiva, nella parte superiore dello schermo viene visualizzata l\'icona <br /> <br /> <img src=vpn_icon />."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> vuole configurare una connessione VPN che le consenta di monitorare il traffico di rete. Accetta soltanto se ritieni la fonte attendibile. Quando la connessione VPN è attiva, sullo schermo viene visualizzata l\'icona <br /> <br /> <img src=vpn_icon />."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN connessa"</string>
<string name="session" msgid="6470628549473641030">"Sessione:"</string>
<string name="duration" msgid="3584782459928719435">"Durata:"</string>
diff --git a/packages/VpnDialogs/res/values-iw/strings.xml b/packages/VpnDialogs/res/values-iw/strings.xml
index ebabd4e..81903d2 100644
--- a/packages/VpnDialogs/res/values-iw/strings.xml
+++ b/packages/VpnDialogs/res/values-iw/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"בקשת חיבור"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> רוצה להגדיר חיבור VPN שיאפשר לו לפקח על תעבורת הרשת. אשר את הבקשה רק אם אתה נותן אמון במקור. <br /> <br /> <img src=vpn_icon /> מופיע בחלק העליון של המסך כאשר VPN פעיל."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"האפליקציה <xliff:g id="APP">%s</xliff:g> מבקשת להגדיר חיבור VPN שבאמצעותו היא תנהל מעקב אחר התנועה ברשת. יש לאשר את הבקשה רק אם המקור נראה לך אמין. <br /> <br /> <img src=vpn_icon /> מופיע על המסך כאשר חיבור ה-VPN פעיל."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN מחובר"</string>
<string name="session" msgid="6470628549473641030">"הפעלה"</string>
<string name="duration" msgid="3584782459928719435">"משך:"</string>
diff --git a/packages/VpnDialogs/res/values-ja/strings.xml b/packages/VpnDialogs/res/values-ja/strings.xml
index 8480692..e03e9d3 100644
--- a/packages/VpnDialogs/res/values-ja/strings.xml
+++ b/packages/VpnDialogs/res/values-ja/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"接続リクエスト"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> がネットワーク トラフィックを監視するため VPN 接続をセットアップしようとしています。信頼できるソースである場合にのみ許可してください。<br /> <br /> VPN がアクティブになると画面の上部に <img src=vpn_icon /> が表示されます。"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> は、ネットワーク トラフィックを監視できるよう、VPN 接続を設定するよう求めています。ソースを信頼できる場合のみ、許可してください。VPN が有効になると、画面に <br /> <br /> <img src=vpn_icon /> が表示されます。"</string>
<string name="legacy_title" msgid="192936250066580964">"VPN接続済み"</string>
<string name="session" msgid="6470628549473641030">"セッション:"</string>
<string name="duration" msgid="3584782459928719435">"期間:"</string>
diff --git a/packages/VpnDialogs/res/values-ka/strings.xml b/packages/VpnDialogs/res/values-ka/strings.xml
index e5a0753..9c4388e 100644
--- a/packages/VpnDialogs/res/values-ka/strings.xml
+++ b/packages/VpnDialogs/res/values-ka/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"კავშირის მოთხოვნა"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> სურს დააყენოს VPN კავშირი, რაც ქსელის ტრაფიკის მონიტორინგის საშუალებას იძლევა. მიიღოთ მხოლოდ ისეთ შემთხვევაში, თუ წყაროს ენდობით. <br /> <br /> <img src=vpn_icon /> თქვენი ეკრანის სიის თავში გამოჩნდება, როდესაც VPN აქტიურია."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g>-ს სურს დააყენოს VPN კავშირი, რაც ქსელის ტრაფიკის მონიტორინგის საშუალებას იძლევა. დათანხმდით მხოლოდ იმ შემთხვევაში, თუ წყაროს ენდობით. <br /> <br /> <img src=vpn_icon /> თქვენს ეკრანზე გამოჩნდება, როდესაც VPN აქტიურია."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN დაკავშირებულია"</string>
<string name="session" msgid="6470628549473641030">"სესია:"</string>
<string name="duration" msgid="3584782459928719435">"ხანგრძლივობა:"</string>
diff --git a/packages/VpnDialogs/res/values-kk/strings.xml b/packages/VpnDialogs/res/values-kk/strings.xml
index 79f79c3..9a499d3 100644
--- a/packages/VpnDialogs/res/values-kk/strings.xml
+++ b/packages/VpnDialogs/res/values-kk/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Байланысты сұрау"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> VPN байланысын орнатқысы келеді, бұл оған желілік трафикті бақылауға мүмкіндік береді. Көзге сенсеңіз ғана қабылдаңыз. VPN белсенді болғанда экранның жоғарғы жағында <br /> <br /> <img src=vpn_icon /> көрсетіледі."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> қолданбасы VPN байланысын орнатқысы келеді, бұл оған желі трафигін бақылауға мүмкіндік береді. Сұрауды қабылдамас бұрын, дереккөздің сенімді екеніне көз жеткізіңіз. VPN белсенді болған кезде, экранда <br /> <br /> <img src=vpn_icon /> белгішесі пайда болады."</string>
<string name="legacy_title" msgid="192936250066580964">"ВЖЖ қосылған"</string>
<string name="session" msgid="6470628549473641030">"Сессия:"</string>
<string name="duration" msgid="3584782459928719435">"Ұзақтығы:"</string>
diff --git a/packages/VpnDialogs/res/values-km/strings.xml b/packages/VpnDialogs/res/values-km/strings.xml
index 06f34db..0ed2e84b 100644
--- a/packages/VpnDialogs/res/values-km/strings.xml
+++ b/packages/VpnDialogs/res/values-km/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"សំណើសុំការតភ្ជាប់"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ចង់បង្កើតការតភ្ជាប់ VPN ដែលអនុញ្ញាតឲ្យវាត្រួតពិនិត្យចរាចរបណ្ដាញ។ ព្រមទទួល ប្រសិនបើអ្នកទុកចិត្តលើប្រភពតែប៉ុណ្ណោះ។ <br /> <br /> <img src=vpn_icon /> នឹងលេចឡើងនៅផ្នែកខាងលើនៃអេក្រង់របស់អ្នក ពេល VPN សកម្ម។"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ចង់រៀបចំការតភ្ជាប់ VPN ដែលអនុញ្ញាតឱ្យវាត្រួតពិនិត្យចរាចរណ៍បណ្តាញ។ យល់ព្រម ប្រសិនបើអ្នកជឿទុកចិត្តលើប្រភពនេះតែប៉ុណ្ណោះ។ <br /> <br /> <img src=vpn_icon /> បង្ហាញនៅលើអេក្រង់របស់អ្នក នៅពេល VPN កំពុងដំណើរការ។"</string>
<string name="legacy_title" msgid="192936250066580964">"បានភ្ជាប់ VPN"</string>
<string name="session" msgid="6470628549473641030">"សម័យ៖"</string>
<string name="duration" msgid="3584782459928719435">"ថិរវេលា៖"</string>
diff --git a/packages/VpnDialogs/res/values-kn/strings.xml b/packages/VpnDialogs/res/values-kn/strings.xml
index 040cd6c..6308f18 100644
--- a/packages/VpnDialogs/res/values-kn/strings.xml
+++ b/packages/VpnDialogs/res/values-kn/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"ಸಂಪರ್ಕ ವಿನಂತಿ"</string>
<string name="warning" msgid="809658604548412033">"ನೆಟ್ವರ್ಕ್ ಟ್ರಾಫಿಕ್ ಅನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಲು ಅನುಮತಿಸುವಂತಹ VPN ಸಂಪರ್ಕವನ್ನು ಹೊಂದಿಸಲು <xliff:g id="APP">%s</xliff:g> ಬಯಸುತ್ತದೆ. ನೀವು ಮೂಲವನ್ನು ನಂಬಿದರೆ ಮಾತ್ರ ಸಮ್ಮತಿಸಿ. VPN ಸಕ್ರಿಯವಾಗಿರುವಾಗ ನಿಮ್ಮ ಪರದೆಯ ಮೇಲ್ಭಾಗದಲ್ಲಿ <br /> <br /> <img src=vpn_icon /> ಗೋರಿಸುತ್ತದೆ."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"ನೆಟ್ವರ್ಕ್ ಟ್ರಾಫಿಕ್ ಅನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಲು ಅನುಮತಿಸುವಂತಹ VPN ಸಂಪರ್ಕವನ್ನು ಹೊಂದಿಸಲು <xliff:g id="APP">%s</xliff:g> ಬಯಸುತ್ತದೆ. ನಿಮಗೆ ಮೂಲದ ಮೇಲೆ ನಂಬಿಕೆ ಇದ್ದರೆ ಮಾತ್ರ ಸ್ವೀಕರಿಸಿ. <br /> <br /> <img src=vpn_icon /> VPN ಸಕ್ರಿಯವಾದ ನಂತರ, ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಮೇಲೆ ಗೋಚರಿಸುತ್ತದೆ."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
<string name="session" msgid="6470628549473641030">"ಸೆಷನ್:"</string>
<string name="duration" msgid="3584782459928719435">"ಅವಧಿ:"</string>
diff --git a/packages/VpnDialogs/res/values-ko/strings.xml b/packages/VpnDialogs/res/values-ko/strings.xml
index 6ad4976..6e179bb 100644
--- a/packages/VpnDialogs/res/values-ko/strings.xml
+++ b/packages/VpnDialogs/res/values-ko/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"연결 요청"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g>에서 네트워크 트래픽을 모니터링하도록 허용하는 VPN 연결을 설정하려고 합니다. 출처를 신뢰할 수 있는 경우에만 수락하세요. VPN이 활성화되면 <br /> <br /> <img src=vpn_icon />이 화면 위에 표시됩니다."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g>에서 네트워크 트래픽을 모니터링할 수 있도록 VPN 연결을 설정하려고 합니다. 소스를 신뢰할 수 있는 경우에만 수락하세요. VPN이 활성 상태일 때는 <br /> <br /> <img src=vpn_icon /> 아이콘이 화면에 표시됩니다."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN이 연결되었습니다."</string>
<string name="session" msgid="6470628549473641030">"세션:"</string>
<string name="duration" msgid="3584782459928719435">"기간:"</string>
diff --git a/packages/VpnDialogs/res/values-ky/strings.xml b/packages/VpnDialogs/res/values-ky/strings.xml
index 23c9be8..31f9e2d 100644
--- a/packages/VpnDialogs/res/values-ky/strings.xml
+++ b/packages/VpnDialogs/res/values-ky/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Туташуу сурамы"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> тармактык трафикти көзөмөлдөөгө уруксат берген VPN туташуусун орноткусу келет. Аны булакка ишенсеңиз гана кабыл алыңыз. <br /> <br /> <img src=vpn_icon /> VPN иштеп турганда экраныңыздын жогору жагынан көрүнөт."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> тармак трафигин көзөмөлдөөгө уруксат берген VPN байланышын орноткусу келет. Булакка ишенсеңиз гана кабыл алыңыз. VPN иштеп жатканда, экраныңызда <br /> <br /> &It;img src=vpn_icon /> көрүнөт."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN байланышта"</string>
<string name="session" msgid="6470628549473641030">"Сессия:"</string>
<string name="duration" msgid="3584782459928719435">"Узактыгы:"</string>
diff --git a/packages/VpnDialogs/res/values-lo/strings.xml b/packages/VpnDialogs/res/values-lo/strings.xml
index c591308..cec69f0 100644
--- a/packages/VpnDialogs/res/values-lo/strings.xml
+++ b/packages/VpnDialogs/res/values-lo/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"ການຮ້ອງຂໍການເຊື່ອມຕໍ່"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ຕ້ອງການຕັ້ງຄ່າການເຊື່ອມຕໍ່ VPN ທີ່ອະນຸຍາດໃຫ້ຕິດຕາມທຣາບຟິກເຄືອຂ່າຍໄດ້. ທ່ານຄວນຍິນຍອມສະເພາະໃນກໍລະນີທີ່ທ່ານເຊື່ອຖືແຫລ່ງຂໍ້ມູນເທົ່ານັ້ນ. <br /> <br /> <img src=vpn_icon /> ຈະປາກົດຢູ່ດ້ານເທິງຂອງໜ້າຈໍເມື່ອມີການເປີດໃຊ້ VPN."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ຕ້ອງການຕັ້ງຄ່າການເຊື່ອມຕໍ່ VPN ທີ່ອະນຸຍາດໃຫ້ມັນກວດກາການຈາລະຈອນເຄືອຂ່າຍໄດ້. ໃຫ້ຍອມຮັບສະເພາະໃນກໍລະນີທີ່ທ່ານເຊື່ອຖືແຫຼ່ງທີ່ມາເທົ່ານັ້ນ. <br /> <br /> <img src=vpn_icon /> ຈະປາກົດຢູ່ໜ້າຈໍຂອງທ່ານເມື່ອເປີດໃຊ້ VPN."</string>
<string name="legacy_title" msgid="192936250066580964">"ເຊື່ອມຕໍ່ VPN ແລ້ວ"</string>
<string name="session" msgid="6470628549473641030">"ເຊສຊັນ:"</string>
<string name="duration" msgid="3584782459928719435">"ໄລຍະເວລາ:"</string>
diff --git a/packages/VpnDialogs/res/values-lt/strings.xml b/packages/VpnDialogs/res/values-lt/strings.xml
index 8846310..97abd0d 100644
--- a/packages/VpnDialogs/res/values-lt/strings.xml
+++ b/packages/VpnDialogs/res/values-lt/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Ryšio užklausa"</string>
<string name="warning" msgid="809658604548412033">"„<xliff:g id="APP">%s</xliff:g>“ nori nustatyti VPN ryšį, kad galėtų stebėti tinklo srautą. Sutikite, tik jei pasitikite šaltiniu. <br /> <br /> <img src=vpn_icon /> rodoma ekrano viršuje, kai VPN aktyvus."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Programa „<xliff:g id="APP">%s</xliff:g>“ nori nustatyti VPN ryšį, kad galėtų stebėti tinklo srautą. Sutikite, tik jei pasitikite šaltiniu. <br /> <br /> <img src=vpn_icon /> piktograma rodoma ekrane, kai VPN aktyvus."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN prijungtas"</string>
<string name="session" msgid="6470628549473641030">"Sesija"</string>
<string name="duration" msgid="3584782459928719435">"Trukmė:"</string>
diff --git a/packages/VpnDialogs/res/values-lv/strings.xml b/packages/VpnDialogs/res/values-lv/strings.xml
index 07625b6..6341fbd 100644
--- a/packages/VpnDialogs/res/values-lv/strings.xml
+++ b/packages/VpnDialogs/res/values-lv/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Savienojuma pieprasījums"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> vēlas izveidot VPN savienojumu, kas ļaus pārraudzīt tīkla datplūsmu. Piekrītiet tikai tad, ja uzticaties avotam. <br /> <br /> <img src=vpn_icon /> tiek rādīta ekrāna augšdaļā, kad darbojas VPN."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Lietotne <xliff:g id="APP">%s</xliff:g> vēlas izveidot VPN savienojumu, kas ļaus pārraudzīt tīkla datplūsmu. Piekrītiet tikai tad, ja uzticaties avotam. <br /> <br /> Ekrānā tiek rādīta ikona <img src=vpn_icon />, kad darbojas VPN."</string>
<string name="legacy_title" msgid="192936250066580964">"Ir izveidots savienojums ar VPN"</string>
<string name="session" msgid="6470628549473641030">"Sesija:"</string>
<string name="duration" msgid="3584782459928719435">"Ilgums:"</string>
diff --git a/packages/VpnDialogs/res/values-mk/strings.xml b/packages/VpnDialogs/res/values-mk/strings.xml
index b5a64f2..689d028 100644
--- a/packages/VpnDialogs/res/values-mk/strings.xml
+++ b/packages/VpnDialogs/res/values-mk/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Барање за поврзување"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> сака да постави поврзување со ВПН коешто му дозволува да го набљудува сообраќајот на мрежата. Прифатете само доколку му верувате на изворот. <br /> <br /> <img src=vpn_icon /> се појавува на врвот на екранот кога ВПН е активна."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> сака да постави поврзување со VPN што ќе дозволи да го набљудува сообраќајот на мрежата. Прифатете само ако му верувате на изворот. <br /> <br /> <img src=vpn_icon /> ќе се појави на екранот кога ќе се активира VPN."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN е поврзана"</string>
<string name="session" msgid="6470628549473641030">"Сесија:"</string>
<string name="duration" msgid="3584782459928719435">"Времетраење:"</string>
@@ -30,7 +31,7 @@
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
<string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"Променете ги поставките за VPN"</string>
<string name="configure" msgid="4905518375574791375">"Конфигурирај"</string>
- <string name="disconnect" msgid="971412338304200056">"Исклучи"</string>
+ <string name="disconnect" msgid="971412338304200056">"Прекини врска"</string>
<string name="open_app" msgid="3717639178595958667">"Отвори ја апликацијата"</string>
<string name="dismiss" msgid="6192859333764711227">"Отфрли"</string>
</resources>
diff --git a/packages/VpnDialogs/res/values-ml/strings.xml b/packages/VpnDialogs/res/values-ml/strings.xml
index 680d0ef..8284a78 100644
--- a/packages/VpnDialogs/res/values-ml/strings.xml
+++ b/packages/VpnDialogs/res/values-ml/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"കണക്ഷൻ അഭ്യർത്ഥന"</string>
<string name="warning" msgid="809658604548412033">"നെറ്റ്വർക്ക് ട്രാഫിക്ക് നിരീക്ഷിക്കാൻ അനുവദിക്കുന്ന ഒരു VPN കണക്ഷൻ <xliff:g id="APP">%s</xliff:g> സജ്ജീകരിക്കേണ്ടതുണ്ട്. ഉറവിടം പരിചിതമാണെങ്കിൽ മാത്രം അംഗീകരിക്കുക. VPN സജീവമാകുമ്പോൾ <br /> <br /> <img src=vpn_icon /> നിങ്ങളുടെ സ്ക്രീനിന്റെ മുകളിൽ ദൃശ്യമാകുന്നു."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"നെറ്റ്വർക്ക് ട്രാഫിക് നിരീക്ഷിക്കാൻ അനുവദിക്കുന്ന ഒരു VPN കണക്ഷൻ സജ്ജീകരിക്കാൻ <xliff:g id="APP">%s</xliff:g> താൽപ്പര്യപ്പെടുന്നു. നിങ്ങൾ ഉറവിടം വിശ്വസിക്കുന്നുണ്ടെങ്കിൽ മാത്രം അംഗീകരിക്കുക. VPN സജീവമാകുമ്പോൾ <br /> <br /> <img src=vpn_icon /> നിങ്ങളുടെ സ്ക്രീനിൽ ദൃശ്യമാകും."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN കണക്റ്റുചെയ്തു"</string>
<string name="session" msgid="6470628549473641030">"സെഷൻ:"</string>
<string name="duration" msgid="3584782459928719435">"സമയദൈര്ഘ്യം:"</string>
diff --git a/packages/VpnDialogs/res/values-mn/strings.xml b/packages/VpnDialogs/res/values-mn/strings.xml
index 9aa104a..1dd4c15 100644
--- a/packages/VpnDialogs/res/values-mn/strings.xml
+++ b/packages/VpnDialogs/res/values-mn/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Холболтын хүсэлт"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> нь сүлжээний трафикыг хянах боломж бүхий VPN холболт үүсгэхийг хүсэж байна. Та зөвхөн эх үүсвэрт итгэж байгаа бол зөвшөөрнө үү. <br /> <br /> <img src=vpn_icon /> таны дэлгэц дээр VPN идэвхтэй үед гарч ирнэ."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> нь түүнд сүлжээний ачааллыг хянах боломжийг олгодог VPN холболт тохируулахыг хүсэж байна. Зөвхөн та эх сурвалжид итгэдэг тохиолдолд зөвшөөрнө үү. VPN идэвхтэй үед таны дэлгэц дээр <br /> <br /> <img src=vpn_icon /> харагдана."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN холбогдов"</string>
<string name="session" msgid="6470628549473641030">"Сешн:"</string>
<string name="duration" msgid="3584782459928719435">"Үргэлжлэх хугацаа:"</string>
diff --git a/packages/VpnDialogs/res/values-mr/strings.xml b/packages/VpnDialogs/res/values-mr/strings.xml
index 41d7429..22fb502 100644
--- a/packages/VpnDialogs/res/values-mr/strings.xml
+++ b/packages/VpnDialogs/res/values-mr/strings.xml
@@ -18,18 +18,19 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"कनेक्शन विनंती"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> नेटवर्क रहदारीचे परीक्षण करण्यासाठी त्यास अनुमती देणारे VPN कनेक्शन सेट करू इच्छितो. तुम्हाला स्रोत विश्वसनीय वाटत असेल तरच स्वीकार करा. <br /> <br /> <img src=vpn_icon /> VPN सक्रिय असताना आपल्या स्क्रीनच्या शीर्षावर दिसते."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ला नेटवर्क ट्रॅफिकवर लक्ष ठेवण्याची अनुमती देणारे VPN कनेक्शन सेट करायचे आहे. तुमचा स्रोतावर विश्वास असेल तरच स्वीकारा. VPN अॅक्टिव्ह असल्यास, तुमच्या स्क्रीनवर <br /> <br /> <img src=vpn_icon /> दिसते."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN कनेक्ट केले"</string>
<string name="session" msgid="6470628549473641030">"सत्र:"</string>
<string name="duration" msgid="3584782459928719435">"कालावधी:"</string>
<string name="data_transmitted" msgid="7988167672982199061">"प्रेषित:"</string>
<string name="data_received" msgid="4062776929376067820">"प्राप्त झाले:"</string>
<string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_0">%1$s</xliff:g> बाइट / <xliff:g id="NUMBER_1">%2$s</xliff:g> पॅकेट"</string>
- <string name="always_on_disconnected_title" msgid="1906740176262776166">"कायम चालू असलेल्या VPN शी कनेक्ट करू शकत नाही"</string>
+ <string name="always_on_disconnected_title" msgid="1906740176262776166">"कायम सुरू असलेल्या VPN शी कनेक्ट करू शकत नाही"</string>
<string name="always_on_disconnected_message" msgid="555634519845992917">"<xliff:g id="VPN_APP_0">%1$s</xliff:g> हे पूर्ण वेळ कनेक्ट राहण्यासाठी सेट अप केलेले आहे, पण हे आता कनेक्ट होऊ शकत नाही. <xliff:g id="VPN_APP_1">%1$s</xliff:g> शी पुन्हा कनेक्ट होईपर्यंत तुमचा फोन सार्वजनिक नेटवर्क वापरेल."</string>
<string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"<xliff:g id="VPN_APP">%1$s</xliff:g> हे पूर्ण वेळ कनेक्ट राहण्यासाठी सेट अप केलेले आहे, पण हे आता कनेक्ट होऊ शकत नाही. VPN पुन्हा कनेक्ट होईपर्यंत तुमच्याकडे कनेक्शन नसेल."</string>
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
<string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"VPN सेटिंग्ज बदला"</string>
- <string name="configure" msgid="4905518375574791375">"कॉन्फिगर करा"</string>
+ <string name="configure" msgid="4905518375574791375">"कॉंफिगर करा"</string>
<string name="disconnect" msgid="971412338304200056">"डिस्कनेक्ट करा"</string>
<string name="open_app" msgid="3717639178595958667">"अॅप उघडा"</string>
<string name="dismiss" msgid="6192859333764711227">"डिसमिस करा"</string>
diff --git a/packages/VpnDialogs/res/values-ms/strings.xml b/packages/VpnDialogs/res/values-ms/strings.xml
index b489f2e..c9961d2 100644
--- a/packages/VpnDialogs/res/values-ms/strings.xml
+++ b/packages/VpnDialogs/res/values-ms/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Permintaan sambungan"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ingin menyediakan sambungan VPN yang membenarkan apl memantau trafik rangkaian. Terima hanya jika anda mempercayai sumber. <br /> <br /> <img src=vpn_icon /> terpapar pada bahagian atas skrin anda apabila VPN aktif."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ingin menyediakan sambungan VPN yang membenarkan apl tersebut memantau trafik rangkaian. Hanya terima jika anda mempercayai sumber tersebut. <br /> <br /> <img src=vpn_icon /> muncul pada skrin anda apabila VPN aktif."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN telah disambungkan"</string>
<string name="session" msgid="6470628549473641030">"Sesi:"</string>
<string name="duration" msgid="3584782459928719435">"Tempoh:"</string>
diff --git a/packages/VpnDialogs/res/values-my/strings.xml b/packages/VpnDialogs/res/values-my/strings.xml
index 9d60ff4..36348c8 100644
--- a/packages/VpnDialogs/res/values-my/strings.xml
+++ b/packages/VpnDialogs/res/values-my/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"ချိတ်ဆက်ရန် တောင်းဆိုချက်"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> က ကွန်ရက် လုပ်ငန်းကို စောင့်ကြည့်ခွင့် ပြုမည့် VPN ချိတ်ဆက်မှုကို ထူထောင်လိုသည်။ ရင်းမြစ်ကို သင်က ယုံကြည်မှသာ လက်ခံပါ။ <br /> <br /> <img src=vpn_icon /> မှာ VPN အလုပ်လုပ်နေလျှင် သင်၏ မျက်နှာပြင် ထိပ်မှာ ပေါ်လာမည်။"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> က ကွန်ရက်ဒေတာ စီးဆင်းမှုကို စောင့်ကြည့်ရန် ခွင့်ပြုသည့် VPN ချိတ်ဆက်မှုကို စနစ်ထည့်သွင်းလိုသည်။ ဤရင်းမြစ်ကို သင်ယုံကြည်မှသာ လက်ခံပါ။ <br /> <br /> <img src=vpn_icon /> သည် VPN ဖွင့်ထားသောအခါ သင့်ဖန်သားပြင်တွင် ပေါ်ပါသည်။"</string>
<string name="legacy_title" msgid="192936250066580964">"VPNနှင့်ချိတ်ဆက်ထားသည်"</string>
<string name="session" msgid="6470628549473641030">"သတ်မှတ်ပေးထားသည့်အချိန်:"</string>
<string name="duration" msgid="3584782459928719435">"အချိန်ကာလ-"</string>
@@ -30,7 +31,7 @@
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
<string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"VPN ဆက်တင်များ ပြောင်းရန်"</string>
<string name="configure" msgid="4905518375574791375">"ပုံပေါ်စေသည်"</string>
- <string name="disconnect" msgid="971412338304200056">"ချိတ်ဆက်ခြင်းရပ်ရန်"</string>
+ <string name="disconnect" msgid="971412338304200056">"ချိတ်ဆက်မှုဖြုတ်ရန်"</string>
<string name="open_app" msgid="3717639178595958667">"အက်ပ်ကို ဖွင့်ရန်"</string>
<string name="dismiss" msgid="6192859333764711227">"ပယ်ရန်"</string>
</resources>
diff --git a/packages/VpnDialogs/res/values-nb/strings.xml b/packages/VpnDialogs/res/values-nb/strings.xml
index be572d4..14c84d7 100644
--- a/packages/VpnDialogs/res/values-nb/strings.xml
+++ b/packages/VpnDialogs/res/values-nb/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Tilkoblingsforespørsel"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ønsker å bruke en VPN-tilkobling som tillater at appen overvåker nettverkstrafikken. Du bør bare godta dette hvis du stoler på kilden. <br /> <br /> <img src=vpn_icon /> vises øverst på skjermen din når VPN er aktivert."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> vil konfigurere en VPN-tilkobling som lar appen overvåke nettverkstrafikk. Du bør bare godta dette hvis du stoler på kilden. <br /> <br /> <img src=vpn_icon /> vises på skjermen når VPN er aktivert."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN er tilkoblet"</string>
<string name="session" msgid="6470628549473641030">"Økt:"</string>
<string name="duration" msgid="3584782459928719435">"Varighet:"</string>
diff --git a/packages/VpnDialogs/res/values-ne/strings.xml b/packages/VpnDialogs/res/values-ne/strings.xml
index b716c35..2a5648d 100644
--- a/packages/VpnDialogs/res/values-ne/strings.xml
+++ b/packages/VpnDialogs/res/values-ne/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"जडान अनुरोध"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ले नेटवर्क यातायात अनुगमन गर्न अनुमति दिने VPN जडान स्थापना गर्न चाहन्छ। तपाईँले स्रोत भरोसा छ भने मात्र स्वीकार गर्नुहोस्। <br /> <br /> <img src=vpn_icon /> जब VPN सक्रिय हुन्छ आफ्नो स्क्रिनको माथि देखा पर्छन्।"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ले कुनै VPN कनेक्सन सेटअप गर्न चाहन्छ। यसको सहायताले यो एप नेटवर्क ट्राफिकको निगरानी राख्न सक्छ। तपाईं यो एपमाथि विश्वास गर्नुहुन्छ भने मात्र स्वीकार गर्नुहोस्। VPN सक्रिय हुँदा तपाईंको स्क्रिनमा <br /> <br /> <img src=vpn_icon /> देखा पर्छ।"</string>
<string name="legacy_title" msgid="192936250066580964">"VPN जोडिएको छ"</string>
<string name="session" msgid="6470628549473641030">"सत्र:"</string>
<string name="duration" msgid="3584782459928719435">"अवधि:"</string>
@@ -30,7 +31,7 @@
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
<string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"VPN सम्बन्धी सेटिङहरू परिवर्तन गर्नुहोस्"</string>
<string name="configure" msgid="4905518375574791375">"कन्फिगर गर्नुहोस्"</string>
- <string name="disconnect" msgid="971412338304200056">"विच्छेदन गर्नुहोस्"</string>
- <string name="open_app" msgid="3717639178595958667">"अनुप्रयोग खोल्नुहोस्"</string>
+ <string name="disconnect" msgid="971412338304200056">"डिस्कनेक्ट गर्नुहोस्"</string>
+ <string name="open_app" msgid="3717639178595958667">"एप खोल्नुहोस्"</string>
<string name="dismiss" msgid="6192859333764711227">"खारेज गर्नुहोस्"</string>
</resources>
diff --git a/packages/VpnDialogs/res/values-nl/strings.xml b/packages/VpnDialogs/res/values-nl/strings.xml
index 8073b09..33f8a89 100644
--- a/packages/VpnDialogs/res/values-nl/strings.xml
+++ b/packages/VpnDialogs/res/values-nl/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Verbindingsverzoek"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wil een VPN-verbinding opzetten om netwerkverkeer te controleren. Accepteer het verzoek alleen als je de bron vertrouwt. <br /> <br /> <img src=vpn_icon /> wordt boven aan je scherm weergegeven wanneer VPN actief is."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> wil een VPN-verbinding instellen waarmee de app het netwerkverkeer kan bijhouden. Accepteer dit alleen als je de bron vertrouwt. <br /> <br /> <img src=vpn_icon /> verschijnt op je scherm als het VPN actief is."</string>
<string name="legacy_title" msgid="192936250066580964">"Verbinding met VPN"</string>
<string name="session" msgid="6470628549473641030">"Sessie:"</string>
<string name="duration" msgid="3584782459928719435">"Duur:"</string>
diff --git a/packages/VpnDialogs/res/values-or/strings.xml b/packages/VpnDialogs/res/values-or/strings.xml
index f1122eb..0604b47 100644
--- a/packages/VpnDialogs/res/values-or/strings.xml
+++ b/packages/VpnDialogs/res/values-or/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"ସଂଯୋଗ ଅନୁରୋଧ"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ଏକ VPN ସଂଯୋଗ ସେଟ୍ ଅପ୍ କରିବାକୁ ଚାହେଁ, ଯାହା ଏହି ନେଟ୍ୱର୍କର ଟ୍ରାଫିକକୁ ମନିଟର୍ କରିବାକୁ ଅନୁମତି ଦିଏ। ଆପଣ ସୋର୍ସ ଉପରେ ବିଶ୍ୱାସ କରିବା ବଦଳରେ କେବଳ ସ୍ୱୀକାର କରନ୍ତୁ। <br /> <br /> <img src=vpn_icon /> VPN ସକ୍ରିୟ ଥିବାବେଳେ ଏହା ଆପଣଙ୍କ ସ୍କ୍ରୀନ୍ର ଉପରେ ଦେଖାଯାଏ।"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ଏକ VPN ସଂଯୋଗ ସେଟ୍ ଅପ୍ କରିବାକୁ ଚାହେଁ, ଯାହା ଏହାକୁ ନେଟୱାର୍କ ଟ୍ରାଫିକ ମନିଟର୍ କରିବାକୁ ଅନୁମତି ଦେଇଥାଏ। ଯଦି ଆପଣ ସୋର୍ସରେ ବିଶ୍ୱାସ କରୁଛନ୍ତି, ତେବେ ହିଁ କେବଳ ସ୍ୱୀକାର କରନ୍ତୁ। <br /> <br /> <img src=vpn_icon /> VPN ସକ୍ରିୟ ଥିବା ସମୟରେ ଆପଣଙ୍କ ସ୍କ୍ରିନ୍ ଉପରେ ଦେଖାଯାଏ।"</string>
<string name="legacy_title" msgid="192936250066580964">"VPN ସଂଯୋଗ ହେଲା"</string>
<string name="session" msgid="6470628549473641030">"ସେସନ୍:"</string>
<string name="duration" msgid="3584782459928719435">"ଅବଧି:"</string>
@@ -28,7 +29,7 @@
<string name="always_on_disconnected_message" msgid="555634519845992917">"<xliff:g id="VPN_APP_0">%1$s</xliff:g> ସବୁ ସମୟରେ କନେକ୍ଟ ହୋଇ ରହିବା ପାଇଁ ସେଟଅପ୍ କରାଯାଇଛି। ଆପଣଙ୍କ ଫୋନ୍, <xliff:g id="VPN_APP_1">%1$s</xliff:g> ସହ କନେକ୍ଟ ନହେବା ପର୍ଯ୍ୟନ୍ତ ଏକ ପବ୍ଲିକ୍ ନେଟ୍ୱର୍କ ବ୍ୟବହାର କରିବ।"</string>
<string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"<xliff:g id="VPN_APP">%1$s</xliff:g> ସବୁ ସମୟରେ କନେକ୍ଟ ହୋଇରହିବାକୁ ସେଟଅପ୍ କରାଯାଇଛି, କିନ୍ତୁ ଏହା ବର୍ତ୍ତମାନ କନେକ୍ଟ କରିପାରୁ ନାହିଁ। VPN ପୁଣି କନେକ୍ଟ ନହେବା ପର୍ଯ୍ୟନ୍ତ ଆପଣଙ୍କର କୌଣସି କନେକ୍ସନ୍ ରହିବନାହିଁ।"</string>
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
- <string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"VPN ସେଟିଙ୍ଗ ବଦଳାନ୍ତୁ"</string>
+ <string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"VPN ସେଟିଂସ୍ ବଦଳାନ୍ତୁ"</string>
<string name="configure" msgid="4905518375574791375">"କନଫିଗର୍ କରନ୍ତୁ"</string>
<string name="disconnect" msgid="971412338304200056">"ବିଚ୍ଛିନ୍ନ କରନ୍ତୁ"</string>
<string name="open_app" msgid="3717639178595958667">"ଆପ୍ ଖୋଲନ୍ତୁ"</string>
diff --git a/packages/VpnDialogs/res/values-pa/strings.xml b/packages/VpnDialogs/res/values-pa/strings.xml
index 1815f4f..d2eba0f 100644
--- a/packages/VpnDialogs/res/values-pa/strings.xml
+++ b/packages/VpnDialogs/res/values-pa/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"ਕਨੈਕਸ਼ਨ ਬੇਨਤੀ"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ਇੱਕ VPN ਕਨੈਕਸ਼ਨ ਸੈਟ ਅਪ ਕਰਨਾ ਚਾਹੁੰਦਾ ਹੈ ਜੋ ਇਸਨੂੰ ਨੈੱਟਵਰਕ ਟ੍ਰੈਫਿਕ ਦਾ ਨਿਰੀਖਣ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਕੇਵਲ ਤਾਂ ਹੀ ਸਵੀਕਾਰ ਕਰੋ ਜੇਕਰ ਤੁਸੀਂ ਸਰੋਤ ਤੇ ਭਰੋਸਾ ਕਰਦੇ ਹੋ। <br /> <br /> <img src=vpn_icon /> ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ ਦੇ ਟੌਪ ਤੇ ਪ੍ਰਗਟ ਹੁੰਦਾ ਹੈ ਜਦੋਂ VPN ਸਕਿਰਿਆ ਹੁੰਦਾ ਹੈ।"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ਕਿਸੇ ਅਜਿਹੇ VPN ਕਨੈਕਸ਼ਨ ਦਾ ਸੈੱਟਅੱਪ ਕਰਨਾ ਚਾਹੁੰਦੀ ਹੈ ਜੋ ਇਸਨੂੰ ਨੈੱਟਵਰਕ ਟਰੈਫ਼ਿਕ ਦੀ ਨਿਗਰਾਨੀ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿੰਦਾ ਹੈ। ਸਿਰਫ਼ ਉਦੋਂ ਹੀ ਸਵੀਕਾਰ ਕਰੋ ਜੇ ਤੁਹਾਨੂੰ ਸਰੋਤ \'ਤੇ ਭਰੋਸਾ ਹੈ। VPN ਦੇ ਕਿਰਿਆਸ਼ੀਲ ਹੋਣ \'ਤੇ <br /> <br /> <img src=vpn_icon /> ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ \'ਤੇ ਦਿਸਦਾ ਹੈ।"</string>
<string name="legacy_title" msgid="192936250066580964">"VPN ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ ਹੈ"</string>
<string name="session" msgid="6470628549473641030">"ਸੈਸ਼ਨ:"</string>
<string name="duration" msgid="3584782459928719435">"ਮਿਆਦ:"</string>
diff --git a/packages/VpnDialogs/res/values-pl/strings.xml b/packages/VpnDialogs/res/values-pl/strings.xml
index d5201d7..82161d3 100644
--- a/packages/VpnDialogs/res/values-pl/strings.xml
+++ b/packages/VpnDialogs/res/values-pl/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Żądanie połączenia"</string>
<string name="warning" msgid="809658604548412033">"Aplikacja <xliff:g id="APP">%s</xliff:g> chce utworzyć połączenie VPN, które pozwoli jej na monitorowanie ruchu sieciowego. Zaakceptuj, tylko jeśli masz zaufanie do źródła. <br /> <br />Gdy sieć VPN jest aktywna, u góry ekranu pojawia się <img src=vpn_icon />."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Aplikacja <xliff:g id="APP">%s</xliff:g> chce utworzyć połączenie VPN, które pozwoli jej na monitorowanie ruchu w sieci. Zaakceptuj, jeśli masz zaufanie do źródła. <br /> <br Gdy sieć VPN jest aktywna, na ekranie pojawia się ikona /> <img src=vpn_icon />."</string>
<string name="legacy_title" msgid="192936250066580964">"Połączono z VPN"</string>
<string name="session" msgid="6470628549473641030">"Sesja:"</string>
<string name="duration" msgid="3584782459928719435">"Czas trwania:"</string>
diff --git a/packages/VpnDialogs/res/values-pt-rBR/strings.xml b/packages/VpnDialogs/res/values-pt-rBR/strings.xml
index 75c1406..0d6dd0b 100644
--- a/packages/VpnDialogs/res/values-pt-rBR/strings.xml
+++ b/packages/VpnDialogs/res/values-pt-rBR/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Solicitação de conexão"</string>
<string name="warning" msgid="809658604548412033">"O <xliff:g id="APP">%s</xliff:g> quer configurar uma conexão VPN que permite monitorar o tráfego da rede. Aceite somente se confiar na origem. <br /> <br /> <img src=vpn_icon /> é exibido na parte superior da tela quando a rede VPN estiver ativa."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"O app <xliff:g id="APP">%s</xliff:g> quer definir uma conexão VPN para monitorar o tráfego da rede. Aceite apenas se você confiar na fonte. O ícone <br /> <br /> <img src=vpn_icon /> aparecerá na tela quando a VPN estiver ativa."</string>
<string name="legacy_title" msgid="192936250066580964">"O VPN está conectado"</string>
<string name="session" msgid="6470628549473641030">"Sessão:"</string>
<string name="duration" msgid="3584782459928719435">"Duração:"</string>
diff --git a/packages/VpnDialogs/res/values-pt-rPT/strings.xml b/packages/VpnDialogs/res/values-pt-rPT/strings.xml
index 01beddb..a310104 100644
--- a/packages/VpnDialogs/res/values-pt-rPT/strings.xml
+++ b/packages/VpnDialogs/res/values-pt-rPT/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Pedido de ligação"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> pretende configurar uma ligação VPN que lhe permita monitorizar o tráfego de rede. Aceite apenas se confiar na fonte. <br /> <br /> <img src=vpn_icon /> aparece na parte superior do seu ecrã quando a VPN está ativa."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"A app <xliff:g id="APP">%s</xliff:g> pretende configurar uma ligação VPN que lhe permita monitorizar o tráfego de rede. Aceite apenas se confiar na origem. <br /> <br /> <img src=vpn_icon /> aparece no ecrã quando a VPN está ativa."</string>
<string name="legacy_title" msgid="192936250066580964">"A VPN está ligada"</string>
<string name="session" msgid="6470628549473641030">"Sessão"</string>
<string name="duration" msgid="3584782459928719435">"Duração:"</string>
@@ -25,12 +26,12 @@
<string name="data_received" msgid="4062776929376067820">"Recebidos:"</string>
<string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_0">%1$s</xliff:g> bytes / <xliff:g id="NUMBER_1">%2$s</xliff:g> pacotes"</string>
<string name="always_on_disconnected_title" msgid="1906740176262776166">"Não é possível estabelecer ligação à VPN sempre ativada"</string>
- <string name="always_on_disconnected_message" msgid="555634519845992917">"A aplicação <xliff:g id="VPN_APP_0">%1$s</xliff:g> está configurada para se manter sempre ligada, mas, neste momento, não é possível estabelecer ligação. O seu telemóvel irá utilizar uma rede pública até conseguir restabelecer ligação à aplicação <xliff:g id="VPN_APP_1">%1$s</xliff:g>."</string>
- <string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"A aplicação <xliff:g id="VPN_APP">%1$s</xliff:g> está configurada para se manter sempre ligada, mas, neste momento, não é possível estabelecer ligação. Não terá ligação até que a VPN a consiga restabelecer."</string>
+ <string name="always_on_disconnected_message" msgid="555634519845992917">"A app <xliff:g id="VPN_APP_0">%1$s</xliff:g> está configurada para se manter sempre ligada, mas, neste momento, não é possível estabelecer ligação. O seu telemóvel irá utilizar uma rede pública até conseguir restabelecer ligação à app <xliff:g id="VPN_APP_1">%1$s</xliff:g>."</string>
+ <string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"A app <xliff:g id="VPN_APP">%1$s</xliff:g> está configurada para se manter sempre ligada, mas, neste momento, não é possível estabelecer ligação. Não terá ligação até que a VPN a consiga restabelecer."</string>
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
<string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"Alterar as definições da VPN"</string>
<string name="configure" msgid="4905518375574791375">"Configurar"</string>
<string name="disconnect" msgid="971412338304200056">"Desligar"</string>
- <string name="open_app" msgid="3717639178595958667">"Abrir aplicação"</string>
+ <string name="open_app" msgid="3717639178595958667">"Abrir app"</string>
<string name="dismiss" msgid="6192859333764711227">"Ignorar"</string>
</resources>
diff --git a/packages/VpnDialogs/res/values-pt/strings.xml b/packages/VpnDialogs/res/values-pt/strings.xml
index 75c1406..0d6dd0b 100644
--- a/packages/VpnDialogs/res/values-pt/strings.xml
+++ b/packages/VpnDialogs/res/values-pt/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Solicitação de conexão"</string>
<string name="warning" msgid="809658604548412033">"O <xliff:g id="APP">%s</xliff:g> quer configurar uma conexão VPN que permite monitorar o tráfego da rede. Aceite somente se confiar na origem. <br /> <br /> <img src=vpn_icon /> é exibido na parte superior da tela quando a rede VPN estiver ativa."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"O app <xliff:g id="APP">%s</xliff:g> quer definir uma conexão VPN para monitorar o tráfego da rede. Aceite apenas se você confiar na fonte. O ícone <br /> <br /> <img src=vpn_icon /> aparecerá na tela quando a VPN estiver ativa."</string>
<string name="legacy_title" msgid="192936250066580964">"O VPN está conectado"</string>
<string name="session" msgid="6470628549473641030">"Sessão:"</string>
<string name="duration" msgid="3584782459928719435">"Duração:"</string>
diff --git a/packages/VpnDialogs/res/values-ro/strings.xml b/packages/VpnDialogs/res/values-ro/strings.xml
index 4e60df2..5bda87e 100644
--- a/packages/VpnDialogs/res/values-ro/strings.xml
+++ b/packages/VpnDialogs/res/values-ro/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Solicitare de conexiune"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> dorește să configureze o conexiune VPN care să îi permită să monitorizeze traficul în rețea. Acceptați numai dacă aveți încredere în sursă. Atunci când conexiunea VPN este activă, <br /> <br /> <img src=vpn_icon /> se afișează în partea de sus a ecranului."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> solicită permisiunea de a configura o conexiune VPN care să îi permită să monitorizeze traficul de rețea. Acceptați numai dacă aveți încredere în sursă. <br /> <br /> <img src=vpn_icon /> va apărea pe ecran atunci când conexiunea VPN este activă."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN este conectat"</string>
<string name="session" msgid="6470628549473641030">"Sesiune:"</string>
<string name="duration" msgid="3584782459928719435">"Durată:"</string>
diff --git a/packages/VpnDialogs/res/values-ru/strings.xml b/packages/VpnDialogs/res/values-ru/strings.xml
index f8fcfb8..ce099562 100644
--- a/packages/VpnDialogs/res/values-ru/strings.xml
+++ b/packages/VpnDialogs/res/values-ru/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Запрос на подключение"</string>
<string name="warning" msgid="809658604548412033">"Приложение \"<xliff:g id="APP">%s</xliff:g>\" пытается подключиться к сети VPN, чтобы отслеживать трафик. Этот запрос следует принимать, только если вы доверяете источнику. <br /><br />Когда подключение к сети VPN активно, в верхней части экрана появляется значок <img src=vpn_icon />."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Приложение \"<xliff:g id="APP">%s</xliff:g>\" пытается подключиться к сети VPN, чтобы отслеживать трафик. Этот запрос следует принимать, только если вы доверяете источнику. Когда подключение к сети VPN активно, на экране появляется значок <br /> <br /> <img src=vpn_icon />."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN-подключение установлено"</string>
<string name="session" msgid="6470628549473641030">"Сеанс:"</string>
<string name="duration" msgid="3584782459928719435">"Продолжительность:"</string>
diff --git a/packages/VpnDialogs/res/values-si/strings.xml b/packages/VpnDialogs/res/values-si/strings.xml
index bb97a5d..a836bae 100644
--- a/packages/VpnDialogs/res/values-si/strings.xml
+++ b/packages/VpnDialogs/res/values-si/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"සම්බන්ධතා ඉල්ලීම"</string>
<string name="warning" msgid="809658604548412033">"ජාල තදබදය නිරීක්ෂණය කිරීමට ඉඩ දෙන VPN සම්බන්ධතාවක් සැකසීමට <xliff:g id="APP">%s</xliff:g> අවශ්යය වේ. ප්රභවය ඔබ විශ්වාස කරන්නේ නම් පමණක් පිළිගන්න. VPN සක්රිය විට <br /> <br /> <img src=vpn_icon />."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"ජාල තදබදය නිරීක්ෂණය කිරීමට ඉඩ දෙන VPN සම්බන්ධතාවක් සැකසීමට <xliff:g id="APP">%s</xliff:g> හට අවශ්ය වේ. ඔබ මූලාශ්රය විශ්වාස කරන්නේ නම් පමණක් පිළිගන්න. <br /> <br /> <img src=vpn_icon /> VPN සක්රිය විට ඔබගේ තිරයෙහි දිස් වේ."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN සම්බන්ධිතයි"</string>
<string name="session" msgid="6470628549473641030">"සැසිය:"</string>
<string name="duration" msgid="3584782459928719435">"කාල සීමාව:"</string>
diff --git a/packages/VpnDialogs/res/values-sk/strings.xml b/packages/VpnDialogs/res/values-sk/strings.xml
index a08117a..766c139 100644
--- a/packages/VpnDialogs/res/values-sk/strings.xml
+++ b/packages/VpnDialogs/res/values-sk/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Žiadosť o pripojenie"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> žiada o nastavenie pripojenia VPN, pomocou ktorého bude môcť sledovať sieťové prenosy. Povoľte iba v prípade, že zdroju dôverujete. <br /> <br /> <img src=vpn_icon /> sa zobrazuje v hornej časti obrazovky, keď je pripojenie VPN aktívne."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Aplikácia <xliff:g id="APP">%s</xliff:g> chce nastaviť pripojenie k sieti VPN, ktoré jej umožňuje sledovať sieťovú premávku. Povoľte to iba v prípade, ak zdroju dôverujete. <br /> <br /> Keď je sieť VPN aktívna, na obrazovke sa zobrazí ikona <img src=vpn_icon />."</string>
<string name="legacy_title" msgid="192936250066580964">"Sieť VPN je pripojená"</string>
<string name="session" msgid="6470628549473641030">"Relácia"</string>
<string name="duration" msgid="3584782459928719435">"Trvanie:"</string>
diff --git a/packages/VpnDialogs/res/values-sl/strings.xml b/packages/VpnDialogs/res/values-sl/strings.xml
index d5014fa34..361a5fa 100644
--- a/packages/VpnDialogs/res/values-sl/strings.xml
+++ b/packages/VpnDialogs/res/values-sl/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Zahteva za povezavo"</string>
<string name="warning" msgid="809658604548412033">"Aplikacija <xliff:g id="APP">%s</xliff:g> želi nastaviti povezavo VPN, ki omogoča nadzor omrežnega prometa. To sprejmite samo, če zaupate viru. Ko je povezava VPN aktivna, se na vrhu zaslona prikaže ikona <br /> <br /> <img src=vpn_icon />."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Aplikacija <xliff:g id="APP">%s</xliff:g> želi nastaviti povezavo VPN, ki ji omogoča nadzor omrežnega prometa. To sprejmite samo, če zaupate viru. Ko je povezava VPN aktivna, je na zaslonu prikazana ikona <br /> <br /> <img src=vpn_icon />."</string>
<string name="legacy_title" msgid="192936250066580964">"Povezava z navideznim zasebnim omrežjem je vzpostavljena"</string>
<string name="session" msgid="6470628549473641030">"Seja:"</string>
<string name="duration" msgid="3584782459928719435">"Trajanje:"</string>
diff --git a/packages/VpnDialogs/res/values-sq/strings.xml b/packages/VpnDialogs/res/values-sq/strings.xml
index 4a96e7b..0b4ce4df 100644
--- a/packages/VpnDialogs/res/values-sq/strings.xml
+++ b/packages/VpnDialogs/res/values-sq/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Kërkesë për lidhje"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> kërkon të vendosë një lidhje VPN-je që e lejon të monitorojë trafikun e rrjetit. Prano vetëm nëse i beson burimit. <br /> <br /> <img src=vpn_icon /> shfaqet në krye të ekranit kur VPN-ja është aktive."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> kërkon të vendosë një lidhje VPN që i lejon të monitorojë trafikun e rrjetit. Pranoje vetëm nëse i beson burimit. <br /> <br /> <img src=vpn_icon /> shfaqet në ekranin tënd kur është aktive VPN."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN-ja është e lidhur"</string>
<string name="session" msgid="6470628549473641030">"Sesioni:"</string>
<string name="duration" msgid="3584782459928719435">"Kohëzgjatja:"</string>
diff --git a/packages/VpnDialogs/res/values-sr/strings.xml b/packages/VpnDialogs/res/values-sr/strings.xml
index 8ce8060..01bd4df 100644
--- a/packages/VpnDialogs/res/values-sr/strings.xml
+++ b/packages/VpnDialogs/res/values-sr/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Захтев за повезивање"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> жели да подеси VPN везу која омогућава праћење саобраћаја на мрежи. Прихватите само ако верујете извору. <br /> <br /> <img src=vpn_icon /> се приказује у врху екрана када је VPN активан."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Апликација <xliff:g id="APP">%s</xliff:g> жели да подеси VPN везу која јој омогућава да прати мрежни саобраћај. Прихватите ово само ако имате поверења у извор. <br /> <br /> <img src=vpn_icon /> се приказује на екрану када је VPN активан."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN је повезан"</string>
<string name="session" msgid="6470628549473641030">"Сесија:"</string>
<string name="duration" msgid="3584782459928719435">"Трајање:"</string>
diff --git a/packages/VpnDialogs/res/values-sv/strings.xml b/packages/VpnDialogs/res/values-sv/strings.xml
index 16b6a31..60ed752 100644
--- a/packages/VpnDialogs/res/values-sv/strings.xml
+++ b/packages/VpnDialogs/res/values-sv/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Anslutningsförfrågan"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> vill starta en VPN-anslutning som tillåter att appen övervakar nätverkstrafiken. Godkänn endast detta om du litar på källan. <br /> <br /> <img src=vpn_icon /> visas längst upp på skärmen när VPN-anslutningen är aktiv."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> vill skapa en VPN-anslutning så att den kan övervaka nätverkstrafik. Godkänn bara om du litar på källan. <br /> <br /> <img src=vpn_icon /> visas på skärmen när VPN-anslutningen är aktiv."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN är anslutet"</string>
<string name="session" msgid="6470628549473641030">"Session:"</string>
<string name="duration" msgid="3584782459928719435">"Längd:"</string>
diff --git a/packages/VpnDialogs/res/values-sw/strings.xml b/packages/VpnDialogs/res/values-sw/strings.xml
index ea26884..c4f4662 100644
--- a/packages/VpnDialogs/res/values-sw/strings.xml
+++ b/packages/VpnDialogs/res/values-sw/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Ombi la muunganisho"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> inataka kusanidi muunganisho wa VPN utakaoiruhusu kufuatilia shughuli kwenye mtandao. Kubali ikiwa tu unakiamini chanzo. <br /> <br /> <img src=vpn_icon /> huonekana sehemu ya juu ya skrini yako VPN inapofanya kazi."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> inagependa kuweka mipangilio ya muunganisho wa VPN inayoiruhusu kufuatilia trafiki ya mtandao. Kubali tu iwapo unaamini chanzo. <br /> <br /> <img src=vpn_icon /> huonekana kwenye skrini yako VPN inapotumika."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN imeunganishwa"</string>
<string name="session" msgid="6470628549473641030">"Kipindi:"</string>
<string name="duration" msgid="3584782459928719435">"Muda:"</string>
diff --git a/packages/VpnDialogs/res/values-ta/strings.xml b/packages/VpnDialogs/res/values-ta/strings.xml
index 3b4cc57..1385bdc 100644
--- a/packages/VpnDialogs/res/values-ta/strings.xml
+++ b/packages/VpnDialogs/res/values-ta/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"இணைப்புக் கோரிக்கை"</string>
<string name="warning" msgid="809658604548412033">"நெட்வொர்க் டிராஃபிக்கைக் கண்காணிக்க வசதியாக VPN இணைப்பை அமைக்க <xliff:g id="APP">%s</xliff:g> கோருகிறது. நம்பகமான மூலத்தை மட்டுமே ஏற்கவும். <br /> <br /> VPN இயக்கத்தில் உள்ளபோது திரையின் மேல் பகுதியில் <img src=vpn_icon /> தோன்றும்."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"நெட்வொர்க் டிராஃபிக்கைக் கண்காணிக்க அனுமதிக்கும் VPN இணைப்பை அமைக்க <xliff:g id="APP">%s</xliff:g> விரும்புகிறது. நம்பகமான VPN ஆப்ஸாக இருந்தால் மட்டுமே ஏற்கவும். <br /> <br /> VPN இயங்கும்போது உங்கள் திரையில் <img src=vpn_icon /> தோன்றும்."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN இணைக்கப்பட்டது"</string>
<string name="session" msgid="6470628549473641030">"அமர்வு:"</string>
<string name="duration" msgid="3584782459928719435">"காலஅளவு:"</string>
diff --git a/packages/VpnDialogs/res/values-te/strings.xml b/packages/VpnDialogs/res/values-te/strings.xml
index 864c926..2316c62 100644
--- a/packages/VpnDialogs/res/values-te/strings.xml
+++ b/packages/VpnDialogs/res/values-te/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"కనెక్షన్ అభ్యర్థన"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> నెట్వర్క్ ట్రాఫిక్ని పర్యవేక్షించగలగడానికి VPN కనెక్షన్ను సెటప్ చేయాలనుకుంటోంది. మీరు మూలాన్ని విశ్వసిస్తే మాత్రమే ఆమోదించండి. VPN సక్రియంగా ఉన్నప్పుడు మీ స్క్రీన్ ఎగువన <br /> <br /> <img src=vpn_icon /> కనిపిస్తుంది."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"నెట్వర్క్ ట్రాఫిక్ను పర్యవేక్షించగలగడానికి, <xliff:g id="APP">%s</xliff:g> VPN కనెక్షన్ను సెటప్ చేయాలనుకుంటోంది. మీరు సోర్స్ను విశ్వసిస్తే మాత్రమే ఆమోదించండి. <br /> <br /> <img src=vpn_icon /> VPN యాక్టివ్గా ఉన్నప్పుడు మీ స్క్రీన్ పై కనిపిస్తుంది."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN కనెక్ట్ చేయబడింది"</string>
<string name="session" msgid="6470628549473641030">"సెషన్:"</string>
<string name="duration" msgid="3584782459928719435">"వ్యవధి:"</string>
diff --git a/packages/VpnDialogs/res/values-th/strings.xml b/packages/VpnDialogs/res/values-th/strings.xml
index 333ff5f..2e174cd 100644
--- a/packages/VpnDialogs/res/values-th/strings.xml
+++ b/packages/VpnDialogs/res/values-th/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"ขอการเชื่อมต่อ"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ต้องการสร้างการเชื่อมต่อ VPN เพื่อให้แอปสามารถตรวจสอบการเข้าใช้งานเครือข่าย โปรดยอมรับหากคุณเชื่อถือแหล่งที่มานี้เท่านั้น <br /> <br /> <img src=vpn_icon /> จะปรากฏที่ด้านบนหน้าจอเมื่อมีการใช้งาน VPN อยู่"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ต้องการตั้งค่าการเชื่อมต่อ VPN เพื่อให้ตรวจสอบการจราจรของข้อมูลในเครือข่ายได้ ยอมรับต่อเมื่อคุณไว้วางใจแหล่งที่มานี้เท่านั้น <br /> <br /> <img src=vpn_icon /> จะปรากฏบนหน้าจอเมื่อใช้งาน VPN อยู่"</string>
<string name="legacy_title" msgid="192936250066580964">"เชื่อมต่อ VPN แล้ว"</string>
<string name="session" msgid="6470628549473641030">"เซสชัน"</string>
<string name="duration" msgid="3584782459928719435">"ระยะเวลา:"</string>
diff --git a/packages/VpnDialogs/res/values-tl/strings.xml b/packages/VpnDialogs/res/values-tl/strings.xml
index 9c01c32..ea69fba 100644
--- a/packages/VpnDialogs/res/values-tl/strings.xml
+++ b/packages/VpnDialogs/res/values-tl/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Kahilingan sa koneksyon"</string>
<string name="warning" msgid="809658604548412033">"Gusto ng <xliff:g id="APP">%s</xliff:g> na mag-set up ng koneksyon sa VPN na nagbibigay-daan ditong masubaybayan ang trapiko ng network. Tanggapin lang kung pinagkakatiwalaan mo ang pinagmulan. Lalabas ang <br /> <br /> <img src=vpn_icon /> sa itaas ng iyong screen kapag aktibo ang VPN."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Gusto ng <xliff:g id="APP">%s</xliff:g> na mag-set up ng koneksyon sa VPN na nagbibigay-daan ditong masubaybayan ang trapiko sa network. Tanggapin lang kung pinagkakatiwalaan mo ang pinagmulan. Lalabas ang <br /> <br /> <img src=vpn_icon /> sa iyong screen kapag aktibo ang VPN."</string>
<string name="legacy_title" msgid="192936250066580964">"Nakakonekta ang VPN"</string>
<string name="session" msgid="6470628549473641030">"Session:"</string>
<string name="duration" msgid="3584782459928719435">"Tagal:"</string>
diff --git a/packages/VpnDialogs/res/values-tr/strings.xml b/packages/VpnDialogs/res/values-tr/strings.xml
index 8665a47..7ffa4bc 100644
--- a/packages/VpnDialogs/res/values-tr/strings.xml
+++ b/packages/VpnDialogs/res/values-tr/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Bağlantı isteği"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ağ trafiğini izlemesine olanak veren bir VPN bağlantısı oluşturmak istiyor. Sadece, ilgili kaynağa güveniyorsanız kabul edin. <br /> <br /> VPN aktif olduğunda ekranınızın üst tarafında <img src=vpn_icon /> simgesi görünür."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g>, ağ trafiğini izlemesine izin veren bir VPN bağlantısı oluşturmak istiyor. Yalnızca kaynağa güveniyorsanız kabul edin. VPN etkin olduğunda ekranınızda <br /> <br /> <img src=vpn_icon /> görünür."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN bağlı"</string>
<string name="session" msgid="6470628549473641030">"Oturum:"</string>
<string name="duration" msgid="3584782459928719435">"Süre:"</string>
diff --git a/packages/VpnDialogs/res/values-uk/strings.xml b/packages/VpnDialogs/res/values-uk/strings.xml
index 8f91abf..6411d7c 100644
--- a/packages/VpnDialogs/res/values-uk/strings.xml
+++ b/packages/VpnDialogs/res/values-uk/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Запит на під’єднання"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> хоче під’єднатися до мережі VPN, щоб контролювати мережевий трафік. Дозволяйте, якщо довіряєте джерелу. Коли мережа VPN активна, угорі екрана відображається значок <br /> <br /> <img src=vpn_icon />."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> хоче під’єднатися до мережі VPN, щоб контролювати мережевий трафік. Надавайте дозвіл, лише якщо довіряєте джерелу. Коли мережа VPN активна, на екрані з’являється значок <br /> <br /> <img src=vpn_icon />."</string>
<string name="legacy_title" msgid="192936250066580964">"Мережу VPN під’єднано"</string>
<string name="session" msgid="6470628549473641030">"Сеанс:"</string>
<string name="duration" msgid="3584782459928719435">"Тривалість:"</string>
diff --git a/packages/VpnDialogs/res/values-ur/strings.xml b/packages/VpnDialogs/res/values-ur/strings.xml
index db0c297..3a23e94 100644
--- a/packages/VpnDialogs/res/values-ur/strings.xml
+++ b/packages/VpnDialogs/res/values-ur/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"کنکشن کی درخواست"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ایک ایسا VPN کنکشن ترتیب دینا چاہتی ہے جو اسے نیٹ ورک ٹریفک کو مانیٹر کرنے کی اجازت دیتا ہے۔ اگر آپ کو ماخذ پر بھروسہ ہے تبھی قبول کریں۔ <br /> <br /> <img src=vpn_icon /> آپ کی اسکرین کے اوپر اس وقت ظاہر ہوتا ہے جب VPN فعال ہوتا ہے۔"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ایک ایسا VPN کنکشن سیٹ اپ کرنا چاہتی ہے جو اسے نیٹ ورک ٹریفک کو مانیٹر کرنے کی اجازت دیتا ہو۔ آپ کو ماخذ پر اعتماد ہونے پر ہی قبول کریں۔ <br /> <br /> <img src=vpn_icon /> VPN کے فعال ہونے پر آپ کی اسکرین پر ظاہر ہوتا ہے۔"</string>
<string name="legacy_title" msgid="192936250066580964">"VPN مربوط ہے"</string>
<string name="session" msgid="6470628549473641030">"سیشن:"</string>
<string name="duration" msgid="3584782459928719435">"دورانیہ:"</string>
diff --git a/packages/VpnDialogs/res/values-uz/strings.xml b/packages/VpnDialogs/res/values-uz/strings.xml
index 5a348a0..a3256e7 100644
--- a/packages/VpnDialogs/res/values-uz/strings.xml
+++ b/packages/VpnDialogs/res/values-uz/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Ulanish uchun so‘rov"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ilovasi trafikni kuzatish uchun VPN tarmog‘iga ulanmoqchi. Agar ilovaga ishonsangiz, so‘rovga rozi bo‘ling.<br /> <br />VPN faol bo‘lsa, ekranning yuqori qismida <img src=vpn_icon /> belgisi paydo bo‘ladi."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ilovasi tarmoqdagi trafikni kuzatish uchun VPN aloqasini sozlamoqchi. Agar unga ishonsangiz, ruxsat bering. VPN aloqa faolligida ekranda <br /> <br /> <img src=vpn_icon /> chiqadi."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN ulangan"</string>
<string name="session" msgid="6470628549473641030">"Seans:"</string>
<string name="duration" msgid="3584782459928719435">"Davomiyligi:"</string>
diff --git a/packages/VpnDialogs/res/values-vi/strings.xml b/packages/VpnDialogs/res/values-vi/strings.xml
index 097c9ae..184d08d 100644
--- a/packages/VpnDialogs/res/values-vi/strings.xml
+++ b/packages/VpnDialogs/res/values-vi/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Yêu cầu kết nối"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> muốn thiết lập kết nối VPN cho phép ứng dụng giám sát lưu lượng truy cập mạng. Chỉ chấp nhận nếu bạn tin tưởng nguồn. <br /> <br /> Biểu tượng <img src=vpn_icon /> xuất hiện ở đầu màn hình của bạn khi VPN đang hoạt động."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> muốn thiết lập kết nối VPN cho phép ứng dụng giám sát lưu lượng truy cập mạng. Bạn chỉ nên chấp nhận nếu tin tưởng nguồn đó. <br /> <br /> <img src=vpn_icon /> sẽ xuất hiện trên màn hình khi VPN đang hoạt động."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN được kết nối"</string>
<string name="session" msgid="6470628549473641030">"Phiên"</string>
<string name="duration" msgid="3584782459928719435">"Thời lượng:"</string>
diff --git a/packages/VpnDialogs/res/values-zh-rCN/strings.xml b/packages/VpnDialogs/res/values-zh-rCN/strings.xml
index 7e528bd..a7262be 100644
--- a/packages/VpnDialogs/res/values-zh-rCN/strings.xml
+++ b/packages/VpnDialogs/res/values-zh-rCN/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"网络连接请求"</string>
<string name="warning" msgid="809658604548412033">"“<xliff:g id="APP">%s</xliff:g>”想要设置一个 VPN 连接,以便监控网络流量。除非您信任该来源,否则请勿接受此请求。<br /> <br />启用 VPN 后,屏幕顶部会出现一个 <img src=vpn_icon /> 图标。"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"“<xliff:g id="APP">%s</xliff:g>”想要建立一个 VPN 连接,以便监控网络流量。除非您信任该来源,否则请不要接受。VPN 处于启用状态时,屏幕上会显示 <br /> <br /> <img src=vpn_icon />。"</string>
<string name="legacy_title" msgid="192936250066580964">"已连接VPN"</string>
<string name="session" msgid="6470628549473641030">"会话:"</string>
<string name="duration" msgid="3584782459928719435">"时长:"</string>
diff --git a/packages/VpnDialogs/res/values-zh-rHK/strings.xml b/packages/VpnDialogs/res/values-zh-rHK/strings.xml
index 49605b0..e4e6432 100644
--- a/packages/VpnDialogs/res/values-zh-rHK/strings.xml
+++ b/packages/VpnDialogs/res/values-zh-rHK/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"連線要求"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> 要求設定 VPN 連線以監控網絡流量。除非您信任要求來源,否則請勿隨意接受要求。<br /> <br />VPN 啟用時,畫面頂端會顯示 <img src=vpn_icon />。"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"「<xliff:g id="APP">%s</xliff:g>」要求設定 VPN 連線以監控網絡流量。除非您信任要求來源,否則請勿隨意接受要求。VPN 啟用時,畫面會顯示 <br /> <br /> <img src=vpn_icon />。"</string>
<string name="legacy_title" msgid="192936250066580964">"VPN 已連線"</string>
<string name="session" msgid="6470628549473641030">"時段:"</string>
<string name="duration" msgid="3584782459928719435">"持續時間︰"</string>
diff --git a/packages/VpnDialogs/res/values-zh-rTW/strings.xml b/packages/VpnDialogs/res/values-zh-rTW/strings.xml
index edd8e61..f54ca4a 100644
--- a/packages/VpnDialogs/res/values-zh-rTW/strings.xml
+++ b/packages/VpnDialogs/res/values-zh-rTW/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"連線要求"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> 要求設定 VPN 連線,允許此要求即開放該來源監控網路流量。除非你信任該來源,否則請勿任意接受要求。<br /> <br />VPN 啟用時,畫面頂端會顯示 <img src=vpn_icon />。"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"「<xliff:g id="APP">%s</xliff:g>」要求設定 VPN 連線,以便監控網路流量。除非你信任該來源,否則請勿接受要求。<br /> <br /> VPN 啟用時,畫面上會顯示 <img src=vpn_icon />。"</string>
<string name="legacy_title" msgid="192936250066580964">"VPN 已連線"</string>
<string name="session" msgid="6470628549473641030">"工作階段:"</string>
<string name="duration" msgid="3584782459928719435">"持續時間:"</string>
diff --git a/packages/VpnDialogs/res/values-zu/strings.xml b/packages/VpnDialogs/res/values-zu/strings.xml
index 4ab1225..c224b13 100644
--- a/packages/VpnDialogs/res/values-zu/strings.xml
+++ b/packages/VpnDialogs/res/values-zu/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Isicelo soxhumo"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ifuna ukusetha uxhumo lwe-VPN eyivumela ukwengamela ithrafikhi yenethiwekhi. Yamukela kuphela uma wethemba umthombo. <br /> <br /> <img src=vpn_icon /> ibonakala phezu kwesikrini sakho uma i-VPN isebenza."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"I-<xliff:g id="APP">%s</xliff:g> ifuna ukusetha uxhumano lwe-VPN oluyivumela ukuthi igade ithrafikhi yenethiwekhi. Yamukela kuphela uma wethemba umthombo. <br /> <br /> <img src=vpn_icon /> ivela kusikrini sakho lapho i-VPN isebenza."</string>
<string name="legacy_title" msgid="192936250066580964">"I-VPN ixhunyiwe"</string>
<string name="session" msgid="6470628549473641030">"Iseshini:"</string>
<string name="duration" msgid="3584782459928719435">"Ubude besikhathi:"</string>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-af/strings.xml
similarity index 65%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-af/strings.xml
index dffb0f0..adc3086 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-af/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Lewer programme onder uitsnede-area"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-am/strings.xml
similarity index 62%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-am/strings.xml
index dffb0f0..648e1d4 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-am/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"ከተቆረጠው አከባቢ በታች የመተግበሪያዎች ምስልን ስራ"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ar/strings.xml
similarity index 62%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-ar/strings.xml
index dffb0f0..2d3b506 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ar/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"عرض التطبيقات أسفل منطقة الصورة المقطوعة للشاشة"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-as/strings.xml
similarity index 61%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-as/strings.xml
index dffb0f0..db2b15a 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-as/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"এপ্সমূহ কাটআউট অঞ্চলৰ তলত দেখুৱাওক"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-az/strings.xml
similarity index 63%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-az/strings.xml
index dffb0f0..a6b7c43 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-az/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Tətbiqləri kəsilmə sahəsinin aşağısında göstərin"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-b+sr+Latn/strings.xml
similarity index 65%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-b+sr+Latn/strings.xml
index dffb0f0..f80fa8d 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-b+sr+Latn/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Prikazuj aplikacije ispod oblasti izreza"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-be/strings.xml
similarity index 63%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-be/strings.xml
index dffb0f0..0e5c8bc 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-be/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Візуалізацыя праграм ніжэй месца выраза"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bg/strings.xml
similarity index 61%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-bg/strings.xml
index dffb0f0..e97bb57 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bg/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Изобразяване на приложенията под областта на прореза"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bn/strings.xml
similarity index 61%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-bn/strings.xml
index dffb0f0..d13c777 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bn/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"কাটআউট এরিয়ার নিচে অ্যাপ রেন্ডার করুন"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bs/strings.xml
similarity index 64%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-bs/strings.xml
index dffb0f0..9c9f437 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bs/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderovanje aplikacija ispod izrezanog područja"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ca/strings.xml
similarity index 63%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-ca/strings.xml
index dffb0f0..e0a577e 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ca/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderitza les aplicacions per sota de l\'àrea de retallada"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-cs/strings.xml
similarity index 65%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-cs/strings.xml
index dffb0f0..0f64473 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-cs/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Vykreslovat aplikace pod oblastí výseče"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-da/strings.xml
similarity index 65%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-da/strings.xml
index dffb0f0..d0cc43e 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-da/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Gengiv apps under skærmhakkets område"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-de/strings.xml
similarity index 64%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-de/strings.xml
index dffb0f0..a7759ea 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-de/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Apps unterhalb des Aussparungs-Bereichs darstellen"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-el/strings.xml
similarity index 62%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-el/strings.xml
index dffb0f0..b71679a 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-el/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Απόδοση εφαρμογών κάτω από την περιοχή εγκοπής"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rAU/strings.xml
similarity index 66%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rAU/strings.xml
index dffb0f0..8c85cbd 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rAU/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Render apps below cutout area"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rCA/strings.xml
similarity index 66%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rCA/strings.xml
index dffb0f0..8c85cbd 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rCA/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Render apps below cutout area"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rGB/strings.xml
similarity index 66%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rGB/strings.xml
index dffb0f0..8c85cbd 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rGB/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Render apps below cutout area"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rIN/strings.xml
similarity index 66%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rIN/strings.xml
index dffb0f0..8c85cbd 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rIN/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Render apps below cutout area"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rXC/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..8b72d9f
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rXC/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Render apps below cutout area"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-es-rUS/strings.xml
similarity index 65%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-es-rUS/strings.xml
index dffb0f0..359cdd0 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-es-rUS/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderizar apps debajo del área de recorte"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-es/strings.xml
similarity index 64%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-es/strings.xml
index dffb0f0..47f525e 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-es/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderizar aplicaciones por debajo de la zona de recorte"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-et/strings.xml
similarity index 64%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-et/strings.xml
index dffb0f0..0cc5a25 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-et/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Väljalõikeala all olevate rakenduste renderdamine"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-eu/strings.xml
similarity index 63%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-eu/strings.xml
index dffb0f0..15d7d60 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-eu/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Errendatu mozketa-eremutik kanpo geratzen diren aplikazioak"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fa/strings.xml
similarity index 62%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-fa/strings.xml
index dffb0f0..0865f75 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fa/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"پرداز زدن برنامهها در زیر ناحیه بریدهشده"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fi/strings.xml
similarity index 65%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-fi/strings.xml
index dffb0f0..1a6bf7a 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fi/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderöi sovellukset lovialueen alle"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fr-rCA/strings.xml
similarity index 64%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-fr-rCA/strings.xml
index dffb0f0..ea0a27b 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fr-rCA/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Rendre les applications sous la zone de découpe"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fr/strings.xml
similarity index 65%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-fr/strings.xml
index dffb0f0..6d91a9d 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fr/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Afficher les applis sous la zone d\'encoche"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-gl/strings.xml
similarity index 64%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-gl/strings.xml
index dffb0f0..382497b 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-gl/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderizar aplicacións que aparezan na zona recortada"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-gu/strings.xml
similarity index 61%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-gu/strings.xml
index dffb0f0..d578d92 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-gu/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"ઍપને કટઆઉટ ક્ષેત્રની નીચે રેન્ડર કરો"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hi/strings.xml
similarity index 61%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-hi/strings.xml
index dffb0f0..e1f09f2 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hi/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"ऐप्लिकेशन को कटआउट एरिया के नीचे दिखाएं"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hr/strings.xml
similarity index 65%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-hr/strings.xml
index dffb0f0..db734e8 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hr/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderiraj aplikacije ispod područja ureza"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hu/strings.xml
similarity index 64%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-hu/strings.xml
index dffb0f0..264095b 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hu/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Alkalmazások megjelenítése a kivágási terület alatt"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hy/strings.xml
similarity index 60%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-hy/strings.xml
index dffb0f0..72e67ec 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hy/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Արտապատկերել հավելվածները էկրանի կտրված հատվածի ներքևում"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-in/strings.xml
similarity index 65%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-in/strings.xml
index dffb0f0..c49bf0c 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-in/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Render aplikasi di bawah area potongan"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-is/strings.xml
similarity index 64%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-is/strings.xml
index dffb0f0..0b90991 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-is/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Birta forrit fyrir neðan útklippta svæðið"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-it/strings.xml
similarity index 65%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-it/strings.xml
index dffb0f0..2a0f026b 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-it/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Visualizza le app sotto l\'area di ritaglio"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-iw/strings.xml
similarity index 63%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-iw/strings.xml
index dffb0f0..cc7a0a4 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-iw/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"עיבוד האפליקציות שמתחת לאזור המגרעת"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ja/strings.xml
similarity index 63%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-ja/strings.xml
index dffb0f0..9e99482 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ja/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"カットアウト領域の下でアプリをレンダリング"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ka/strings.xml
similarity index 62%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-ka/strings.xml
index dffb0f0..5464a56 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ka/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"აპების ასახვა ჭრილის ქვემოთ"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-kk/strings.xml
similarity index 62%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-kk/strings.xml
index dffb0f0..6a2623f 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-kk/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Экран ойығының астындағы қолданбаларды көрсету"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-km/strings.xml
similarity index 62%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-km/strings.xml
index dffb0f0..4b4d169 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-km/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"បំប្លែងកម្មវិធីខាងក្រោមផ្នែកឆក"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-kn/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-kn/strings.xml
new file mode 100644
index 0000000..7a929d1
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-kn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"ಕಟೌಟ್ ಪ್ರದೇಶದ ಕೆಳಗಿನ ಆ್ಯಪ್ಗಳನ್ನು ರೆಂಡರ್ ಮಾಡಿ"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ko/strings.xml
similarity index 65%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-ko/strings.xml
index dffb0f0..4b9e640 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ko/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"컷아웃 영역 아래에 앱 렌더링"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ky/strings.xml
similarity index 61%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-ky/strings.xml
index dffb0f0..1ac6a8b 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ky/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Колдонмолорду кесилген аймактын ылдый жагында көрсөтүү"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lo/strings.xml
similarity index 60%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-lo/strings.xml
index dffb0f0..4c38580 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lo/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"ສະແດງພາບແອັບຢູ່ທາງລຸ່ມພື້ນທີ່ຮອຍເສັ້ນ"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lt/strings.xml
similarity index 65%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-lt/strings.xml
index dffb0f0..c43736d 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lt/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Pateikti programas po išpjovos sritimi"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lv/strings.xml
similarity index 65%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-lv/strings.xml
index dffb0f0..f95abb6 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lv/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Atveidot lietotnes zem izgriezuma apgabala"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mk/strings.xml
similarity index 62%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-mk/strings.xml
index dffb0f0..ff236be 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mk/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Прикажувај апликации под отсечената област"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ml/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ml/strings.xml
new file mode 100644
index 0000000..ef728ab
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ml/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"കട്ടൗട്ട് ഭാഗത്തിന് താഴെ ആപ്പുകൾ റെൻഡർ ചെയ്യുക"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mn/strings.xml
similarity index 62%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-mn/strings.xml
index dffb0f0..23dbe0c 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mn/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Аппуудыг тасалж авсан хэсгийн доор буулгах"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mr/strings.xml
similarity index 60%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-mr/strings.xml
index dffb0f0..42f09cb 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mr/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"कटआउट क्षेत्राच्या खाली असलेली ॲप्स रेंडर करा"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ms/strings.xml
similarity index 65%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-ms/strings.xml
index dffb0f0..e348630 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ms/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Serahkan apl di bawah kawasan potongan"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-my/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-my/strings.xml
new file mode 100644
index 0000000..90cb0a5
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-my/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"ဖြတ်ထုတ်ထားသော နေရာအောက်ရှိ အက်ပ်များ ပြသရန်"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-nb/strings.xml
similarity index 65%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-nb/strings.xml
index dffb0f0..b8b4e75 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-nb/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Gjengi apper under utklippsområdet"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ne/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ne/strings.xml
new file mode 100644
index 0000000..bd213bb
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ne/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"कटआउट गरिएको क्षेत्रभन्दा तल पर्ने एपहरू रेन्डर गर्नुहोस्"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-nl/strings.xml
similarity index 65%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-nl/strings.xml
index dffb0f0..68f5c07 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-nl/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Apps renderen onder display-cutout"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-or/strings.xml
similarity index 60%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-or/strings.xml
index dffb0f0..162a29e 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-or/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"ଆପଗୁଡ଼ିକୁ କଟଆଉଟ୍ ଏରିଆ ନିମ୍ନରେ ରେଣ୍ଡର୍ କରନ୍ତୁ"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pa/strings.xml
similarity index 62%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-pa/strings.xml
index dffb0f0..908393b 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pa/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"ਕੱਟਆਊਟ ਖੇਤਰ ਹੇਠ ਐਪਾਂ ਨੂੰ ਰੈਂਡਰ ਕਰੋ"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pl/strings.xml
similarity index 65%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-pl/strings.xml
index dffb0f0..c027d52 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pl/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderuj aplikacje pod obszarem wycięcia"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt-rBR/strings.xml
similarity index 65%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt-rBR/strings.xml
index dffb0f0..d09ed97 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt-rBR/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderizar apps abaixo da área de corte"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt-rPT/strings.xml
similarity index 65%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt-rPT/strings.xml
index dffb0f0..d38ce43 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt-rPT/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderizar apps abaixo da área de recorte"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt/strings.xml
similarity index 65%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt/strings.xml
index dffb0f0..d09ed97 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderizar apps abaixo da área de corte"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ro/strings.xml
similarity index 65%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-ro/strings.xml
index dffb0f0..6e5947c 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ro/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Redați aplicațiile sub zona de decupaj"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ru/strings.xml
similarity index 63%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-ru/strings.xml
index dffb0f0..c7f54bb 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ru/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Отображать приложения под вырезом"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-si/strings.xml
similarity index 61%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-si/strings.xml
index dffb0f0..4a14a36 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-si/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"කටවුට් ප්රදේශයට පහළින් යෙදුම් විදහන්න"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sk/strings.xml
similarity index 64%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-sk/strings.xml
index dffb0f0..98b82e6 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sk/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Vykresľovať aplikácie pod oblasťou výrezu"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sl/strings.xml
similarity index 64%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-sl/strings.xml
index dffb0f0..dcf0c84 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sl/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Upodobitev aplikacij pod predelom zareze zaslona"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sq/strings.xml
similarity index 64%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-sq/strings.xml
index dffb0f0..d7b0676 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sq/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Paraqiti aplikacionet poshtë zonës së prerjes"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sr/strings.xml
similarity index 62%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-sr/strings.xml
index dffb0f0..c2b611e 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sr/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Приказуј апликације испод области изреза"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sv/strings.xml
similarity index 64%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-sv/strings.xml
index dffb0f0..3007ffb 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sv/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Visa appar under skärmutskärningens område"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sw/strings.xml
similarity index 64%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-sw/strings.xml
index dffb0f0..b605554 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sw/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Usionyeshe programu chini ya eneo lenye pengo"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ta/strings.xml
similarity index 60%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-ta/strings.xml
index dffb0f0..c4d06fb 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ta/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"கட் அவுட் பகுதிக்குள்ளாக ஆப்ஸை ரெண்டர் செய்"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-te/strings.xml
similarity index 60%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-te/strings.xml
index dffb0f0..08fa4ae 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-te/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"కట్అవుట్ ఏరియా కింద యాప్లను రెండర్ చేయండి"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-th/strings.xml
similarity index 63%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-th/strings.xml
index dffb0f0..9a30250 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-th/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"แสดงผลแอปใต้บริเวณรอยบาก"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-tl/strings.xml
similarity index 64%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-tl/strings.xml
index dffb0f0..a3d4a3a 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-tl/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"I-render ang mga app sa ibaba ng lugar ng cutout"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-tr/strings.xml
similarity index 64%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-tr/strings.xml
index dffb0f0..12e0f30 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-tr/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Uygulamaları kesme alanının altında oluşturun"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-uk/strings.xml
similarity index 62%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-uk/strings.xml
index dffb0f0..08b1521 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-uk/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Відображати додатки під областю вирізу екрана"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ur/strings.xml
similarity index 64%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-ur/strings.xml
index dffb0f0..711b538 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ur/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"کٹ آؤٹ ایریا کے نیچے رینڈر ایپس"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-uz/strings.xml
similarity index 65%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-uz/strings.xml
index dffb0f0..7f6f2b4 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-uz/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Ekran kesimi quyidagi ilovalarni renderlash"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-vi/strings.xml
similarity index 63%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-vi/strings.xml
index dffb0f0..a7d54fb 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-vi/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Hiển thị các ứng dụng bên dưới khu vực có vết cắt"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rCN/strings.xml
similarity index 65%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rCN/strings.xml
index dffb0f0..f596520 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rCN/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"在刘海区域下方呈现应用"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rHK/strings.xml
similarity index 65%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rHK/strings.xml
index dffb0f0..ddb1df7 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rHK/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"在凹口區域下方輸出應用程式"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rTW/strings.xml
similarity index 64%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rTW/strings.xml
index dffb0f0..7aad79c 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rTW/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"在螢幕凹口底下顯示應用程式畫面"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zu/strings.xml
similarity index 64%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/AvoidAppsInCutoutOverlay/res/values-zu/strings.xml
index dffb0f0..d861c5e 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zu/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Nikezela ngama-app angaphansi kwendawo yokukhipha"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-af/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-af/strings.xml
index dffb0f0..b021da7 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-af/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Versteek"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-am/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-am/strings.xml
index dffb0f0..0ca6135 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-am/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"ደብቅ"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-ar/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-ar/strings.xml
index dffb0f0..7e41cb1 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-ar/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"إخفاء"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-as/strings.xml
similarity index 66%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-as/strings.xml
index dffb0f0..d2399ff 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-as/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"লুকুৱাওক"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-az/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-az/strings.xml
index dffb0f0..420c513 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-az/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Gizlədin"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-b+sr+Latn/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-b+sr+Latn/strings.xml
index dffb0f0..082e586 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-b+sr+Latn/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Sakrij"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-be/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-be/strings.xml
index dffb0f0..ce75c45 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-be/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Схаваць"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-bg/strings.xml
similarity index 66%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-bg/strings.xml
index dffb0f0..7c3170c 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-bg/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Скриване"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-bn/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-bn/strings.xml
index dffb0f0..1e62725 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-bn/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"লুকান"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-bs/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-bs/strings.xml
index dffb0f0..082e586 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-bs/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Sakrij"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-ca/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-ca/strings.xml
index dffb0f0..6ae5ffd 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-ca/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Amaga"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-cs/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-cs/strings.xml
index dffb0f0..068bb94 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-cs/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Skrýt"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-da/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-da/strings.xml
index dffb0f0..6ecb767 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-da/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Skjul"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-de/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-de/strings.xml
index dffb0f0..44278e8 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-de/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ausblenden"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-el/strings.xml
similarity index 66%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-el/strings.xml
index dffb0f0..96b1b86 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-el/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Απόκρυψη"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-en-rAU/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-en-rAU/strings.xml
index dffb0f0..1ab9b2d 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-en-rAU/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Hide"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-en-rCA/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-en-rCA/strings.xml
index dffb0f0..1ab9b2d 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-en-rCA/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Hide"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-en-rGB/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-en-rGB/strings.xml
index dffb0f0..1ab9b2d 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-en-rGB/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Hide"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-en-rIN/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-en-rIN/strings.xml
index dffb0f0..1ab9b2d 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-en-rIN/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Hide"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-en-rXC/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..a20e594
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-en-rXC/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Hide"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-es-rUS/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-es-rUS/strings.xml
index dffb0f0..351c1cd 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-es-rUS/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ocultar"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-es/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-es/strings.xml
index dffb0f0..351c1cd 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-es/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ocultar"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-et/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-et/strings.xml
index dffb0f0..4e5428d 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-et/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Peida"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-eu/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-eu/strings.xml
index dffb0f0..f33bb50 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-eu/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ezkutatu"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-fa/strings.xml
similarity index 66%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-fa/strings.xml
index dffb0f0..8de7560 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-fa/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"پنهان کردن"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-fi/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-fi/strings.xml
index dffb0f0..c42e52d 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-fi/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Piilota"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-fr-rCA/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-fr-rCA/strings.xml
index dffb0f0..31fa567 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-fr-rCA/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Masquer"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-fr/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-fr/strings.xml
index dffb0f0..31fa567 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-fr/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Masquer"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-gl/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-gl/strings.xml
index dffb0f0..351c1cd 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-gl/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ocultar"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-gu/strings.xml
similarity index 66%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-gu/strings.xml
index dffb0f0..7e4b33a 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-gu/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"છુપાવો"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-hi/strings.xml
similarity index 66%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-hi/strings.xml
index dffb0f0..1513f2a 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-hi/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"छिपाएं"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-hr/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-hr/strings.xml
index dffb0f0..082e586 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-hr/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Sakrij"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-hu/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-hu/strings.xml
index dffb0f0..2b9717f 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-hu/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Elrejtés"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-hy/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-hy/strings.xml
index dffb0f0..3407bb4 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-hy/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Թաքցնել"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-in/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-in/strings.xml
index dffb0f0..6c017b3 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-in/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Sembunyikan"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-is/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-is/strings.xml
index dffb0f0..229c113 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-is/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Fela"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-it/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-it/strings.xml
index dffb0f0..07444aa 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-it/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Nascondi"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-iw/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-iw/strings.xml
index dffb0f0..221a129 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-iw/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"הסתרה"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-ja/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-ja/strings.xml
index dffb0f0..3bf17fb 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-ja/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"非表示"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-ka/strings.xml
similarity index 66%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-ka/strings.xml
index dffb0f0..1600528 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-ka/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"დამალვა"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-kk/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-kk/strings.xml
index dffb0f0..23d02b8 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-kk/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Жасыру"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-km/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-km/strings.xml
index dffb0f0..624b81f 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-km/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"លាក់"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-kn/strings.xml
similarity index 66%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-kn/strings.xml
index dffb0f0..9cd6da7 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-kn/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"ಮರೆಮಾಡಿ"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-ko/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-ko/strings.xml
index dffb0f0..efb6568 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-ko/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"숨기기"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-ky/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-ky/strings.xml
index dffb0f0..4191325 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-ky/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Жашыруу"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-lo/strings.xml
similarity index 66%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-lo/strings.xml
index dffb0f0..8850dfb 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-lo/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"ເຊື່ອງ"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-lt/strings.xml
similarity index 67%
rename from packages/SystemUI/res/layout/global_actions_change_panel.xml
rename to packages/overlays/NoCutoutOverlay/res/values-lt/strings.xml
index dffb0f0..6364b96 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-lt/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Slėpti"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-lv/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-lv/strings.xml
index dffb0f0..61f2ad3 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-lv/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Paslēpt"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-mk/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-mk/strings.xml
index dffb0f0..505c205 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-mk/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Сокриј"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-ml/strings.xml
similarity index 66%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-ml/strings.xml
index dffb0f0..1c30dec 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-ml/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"മറയ്ക്കുക"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-mn/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-mn/strings.xml
index dffb0f0..7e2719b 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-mn/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Нуух"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-mr/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-mr/strings.xml
index dffb0f0..46f0ab8 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-mr/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"लपवा"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-ms/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-ms/strings.xml
index dffb0f0..6c017b3 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-ms/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Sembunyikan"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-my/strings.xml
similarity index 66%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-my/strings.xml
index dffb0f0..84be3f9 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-my/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"ဝှက်ရန်"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-nb/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-nb/strings.xml
index dffb0f0..6ecb767 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-nb/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Skjul"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-ne/strings.xml
similarity index 66%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-ne/strings.xml
index dffb0f0..ff920b2 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-ne/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"लुकाइयोस्"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-nl/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-nl/strings.xml
index dffb0f0..00900f8 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-nl/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Verbergen"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-or/strings.xml
similarity index 66%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-or/strings.xml
index dffb0f0..fcfd725 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-or/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"ଲୁଚାନ୍ତୁ"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-pa/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-pa/strings.xml
index dffb0f0..9f37e0b 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-pa/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"ਲੁਕਾਓ"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-pl/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-pl/strings.xml
index dffb0f0..3d65546 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-pl/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ukryj"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-pt-rBR/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-pt-rBR/strings.xml
index dffb0f0..351c1cd 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-pt-rBR/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ocultar"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-pt-rPT/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-pt-rPT/strings.xml
index dffb0f0..351c1cd 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-pt-rPT/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ocultar"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-pt/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-pt/strings.xml
index dffb0f0..351c1cd 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-pt/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ocultar"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-ro/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-ro/strings.xml
index dffb0f0..e6281fd 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-ro/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ascundeți"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-ru/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-ru/strings.xml
index dffb0f0..9018396 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-ru/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Скрыть"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-si/strings.xml
similarity index 66%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-si/strings.xml
index dffb0f0..b06a52c 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-si/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"සඟවන්න"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-sk/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-sk/strings.xml
index dffb0f0..965d95b 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-sk/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Skryť"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-sl/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-sl/strings.xml
index dffb0f0..e8adb98 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-sl/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Skrij"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-sq/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-sq/strings.xml
index dffb0f0..85fb95a 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-sq/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Fshih"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-sr/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-sr/strings.xml
index dffb0f0..26afbf9 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-sr/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Сакриј"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-sv/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-sv/strings.xml
index dffb0f0..193c179 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-sv/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Dölj"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-sw/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-sw/strings.xml
index dffb0f0..58e35e2 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-sw/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ficha"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-ta/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-ta/strings.xml
index dffb0f0..b743c66 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-ta/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"மறை"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-te/strings.xml
similarity index 66%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-te/strings.xml
index dffb0f0..de04152 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-te/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"దాచండి"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-th/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-th/strings.xml
index dffb0f0..1c8bd9d 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-th/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"ซ่อน"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-tl/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-tl/strings.xml
index dffb0f0..cc45f63 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-tl/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Itago"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-tr/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-tr/strings.xml
index dffb0f0..b20f1f7 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-tr/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Gizle"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-uk/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-uk/strings.xml
index dffb0f0..938b0e2 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-uk/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Сховати"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-ur/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-ur/strings.xml
index dffb0f0..0f08170 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-ur/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"چھپائیں"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-uz/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-uz/strings.xml
index dffb0f0..5d22045 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-uz/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Berkitish"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-vi/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-vi/strings.xml
index dffb0f0..fc83d25 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-vi/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ẩn"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-zh-rCN/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-zh-rCN/strings.xml
index dffb0f0..acdfd1c 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-zh-rCN/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"隐藏"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-zh-rHK/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-zh-rHK/strings.xml
index dffb0f0..abb8e81 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-zh-rHK/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"隱藏"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-zh-rTW/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-zh-rTW/strings.xml
index dffb0f0..abb8e81 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-zh-rTW/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"隱藏"</string>
+</resources>
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/overlays/NoCutoutOverlay/res/values-zu/strings.xml
similarity index 67%
copy from packages/SystemUI/res/layout/global_actions_change_panel.xml
copy to packages/overlays/NoCutoutOverlay/res/values-zu/strings.xml
index dffb0f0..11842d9 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-zu/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,9 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
- -->
-<ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Fihla"</string>
+</resources>
diff --git a/rs/java/android/renderscript/ScriptIntrinsicBlend.java b/rs/java/android/renderscript/ScriptIntrinsicBlend.java
index a1c79ef..3109cd8 100644
--- a/rs/java/android/renderscript/ScriptIntrinsicBlend.java
+++ b/rs/java/android/renderscript/ScriptIntrinsicBlend.java
@@ -104,7 +104,7 @@
* @param opt LaunchOptions for clipping
*/
public void forEachSrc(Allocation ain, Allocation aout, Script.LaunchOptions opt) {
- blend(1, ain, aout, null);
+ blend(1, ain, aout, opt);
}
/**
@@ -641,4 +641,3 @@
}
*/
}
-
diff --git a/services/Android.bp b/services/Android.bp
index c83a697..cc0fd98 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -130,6 +130,7 @@
libs: [
"android.hidl.manager-V1.0-java",
"framework-tethering.stubs.module_lib",
+ "service-art.stubs.system_server",
],
// Uncomment to enable output of certain warnings (deprecated, unchecked)
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index e9c9899..ee80dae 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -21,8 +21,13 @@
import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_STATUS;
import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP;
import static android.accessibilityservice.AccessibilityServiceInfo.DEFAULT;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CONNECTION;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
import static android.view.accessibility.AccessibilityInteractionClient.CALL_STACK;
+import static android.view.accessibility.AccessibilityInteractionClient.IGNORE_CALL_STACK;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
@@ -31,6 +36,7 @@
import android.accessibilityservice.AccessibilityGestureEvent;
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.accessibilityservice.IAccessibilityServiceConnection;
import android.annotation.NonNull;
@@ -103,10 +109,9 @@
FingerprintGestureDispatcher.FingerprintGestureClient {
private static final boolean DEBUG = false;
private static final String LOG_TAG = "AbstractAccessibilityServiceConnection";
- private static final String TRACE_A11Y_SERVICE_CONNECTION =
- LOG_TAG + ".IAccessibilityServiceConnection";
- private static final String TRACE_A11Y_SERVICE_CLIENT =
- LOG_TAG + ".IAccessibilityServiceClient";
+ private static final String TRACE_SVC_CONN = LOG_TAG + ".IAccessibilityServiceConnection";
+ private static final String TRACE_SVC_CLIENT = LOG_TAG + ".IAccessibilityServiceClient";
+ private static final String TRACE_WM = "WindowManagerInternal";
private static final int WAIT_WINDOWS_TIMEOUT_MILLIS = 5000;
protected static final String TAKE_SCREENSHOT = "takeScreenshot";
@@ -298,9 +303,8 @@
return false;
}
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onKeyEvent",
- keyEvent + ", " + sequenceNumber);
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("onKeyEvent", keyEvent + ", " + sequenceNumber);
}
mServiceInterface.onKeyEvent(keyEvent, sequenceNumber);
} catch (RemoteException e) {
@@ -365,17 +369,16 @@
@Override
public void setOnKeyEventResult(boolean handled, int sequence) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setOnKeyEventResult",
- "handled=" + handled + ";sequence=" + sequence);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("setOnKeyEventResult", "handled=" + handled + ";sequence=" + sequence);
}
mSystemSupport.getKeyEventDispatcher().setOnKeyEventResult(this, handled, sequence);
}
@Override
public AccessibilityServiceInfo getServiceInfo() {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getServiceInfo");
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("getServiceInfo", "");
}
synchronized (mLock) {
return mAccessibilityServiceInfo;
@@ -393,8 +396,8 @@
@Override
public void setServiceInfo(AccessibilityServiceInfo info) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setServiceInfo", "info=" + info);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("setServiceInfo", "info=" + info);
}
final long identity = Binder.clearCallingIdentity();
try {
@@ -421,8 +424,8 @@
@Nullable
@Override
public AccessibilityWindowInfo.WindowListSparseArray getWindows() {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getWindows");
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("getWindows", "");
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
@@ -458,8 +461,8 @@
@Override
public AccessibilityWindowInfo getWindow(int windowId) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getWindow", "windowId=" + windowId);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("getWindow", "windowId=" + windowId);
}
synchronized (mLock) {
int displayId = Display.INVALID_DISPLAY;
@@ -496,8 +499,8 @@
long accessibilityNodeId, String viewIdResName, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".findAccessibilityNodeInfosByViewId",
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("findAccessibilityNodeInfosByViewId",
"accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
+ accessibilityNodeId + ";viewIdResName=" + viewIdResName + ";interactionId="
+ interactionId + ";callback=" + callback + ";interrogatingTid="
@@ -539,6 +542,12 @@
callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
interrogatingPid, interrogatingTid);
final long identityToken = Binder.clearCallingIdentity();
+ if (intConnTracingEnabled()) {
+ logTraceIntConn("findAccessibilityNodeInfosByViewId",
+ accessibilityNodeId + ";" + viewIdResName + ";" + partialInteractiveRegion + ";"
+ + interactionId + ";" + callback + ";" + mFetchFlags + ";" + interrogatingPid
+ + ";" + interrogatingTid + ";" + spec);
+ }
try {
connection.getRemote().findAccessibilityNodeInfosByViewId(accessibilityNodeId,
viewIdResName, partialInteractiveRegion, interactionId, callback, mFetchFlags,
@@ -564,8 +573,8 @@
long accessibilityNodeId, String text, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".findAccessibilityNodeInfosByText",
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("findAccessibilityNodeInfosByText",
"accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
+ accessibilityNodeId + ";text=" + text + ";interactionId=" + interactionId
+ ";callback=" + callback + ";interrogatingTid=" + interrogatingTid);
@@ -606,6 +615,12 @@
callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
interrogatingPid, interrogatingTid);
final long identityToken = Binder.clearCallingIdentity();
+ if (intConnTracingEnabled()) {
+ logTraceIntConn("findAccessibilityNodeInfosByText",
+ accessibilityNodeId + ";" + text + ";" + partialInteractiveRegion + ";"
+ + interactionId + ";" + callback + ";" + mFetchFlags + ";" + interrogatingPid
+ + ";" + interrogatingTid + ";" + spec);
+ }
try {
connection.getRemote().findAccessibilityNodeInfosByText(accessibilityNodeId,
text, partialInteractiveRegion, interactionId, callback, mFetchFlags,
@@ -631,13 +646,12 @@
int accessibilityWindowId, long accessibilityNodeId, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags,
long interrogatingTid, Bundle arguments) throws RemoteException {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(
- TRACE_A11Y_SERVICE_CONNECTION + ".findAccessibilityNodeInfoByAccessibilityId",
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("findAccessibilityNodeInfoByAccessibilityId",
"accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
- + accessibilityNodeId + ";interactionId=" + interactionId + ";callback="
- + callback + ";flags=" + flags + ";interrogatingTid=" + interrogatingTid
- + ";arguments=" + arguments);
+ + accessibilityNodeId + ";interactionId=" + interactionId + ";callback="
+ + callback + ";flags=" + flags + ";interrogatingTid=" + interrogatingTid
+ + ";arguments=" + arguments);
}
final int resolvedWindowId;
RemoteAccessibilityConnection connection;
@@ -675,6 +689,12 @@
callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
interrogatingPid, interrogatingTid);
final long identityToken = Binder.clearCallingIdentity();
+ if (intConnTracingEnabled()) {
+ logTraceIntConn("findAccessibilityNodeInfoByAccessibilityId",
+ accessibilityNodeId + ";" + partialInteractiveRegion + ";" + interactionId + ";"
+ + callback + ";" + (mFetchFlags | flags) + ";" + interrogatingPid + ";"
+ + interrogatingTid + ";" + spec + ";" + arguments);
+ }
try {
connection.getRemote().findAccessibilityNodeInfoByAccessibilityId(
accessibilityNodeId, partialInteractiveRegion, interactionId, callback,
@@ -700,12 +720,12 @@
int focusType, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".findFocus",
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("findFocus",
"accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
- + accessibilityNodeId + ";focusType=" + focusType + ";interactionId="
- + interactionId + ";callback=" + callback + ";interrogatingTid="
- + interrogatingTid);
+ + accessibilityNodeId + ";focusType=" + focusType + ";interactionId="
+ + interactionId + ";callback=" + callback + ";interrogatingTid="
+ + interrogatingTid);
}
final int resolvedWindowId;
RemoteAccessibilityConnection connection;
@@ -743,6 +763,12 @@
callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
interrogatingPid, interrogatingTid);
final long identityToken = Binder.clearCallingIdentity();
+ if (intConnTracingEnabled()) {
+ logTraceIntConn("findFocus",
+ accessibilityNodeId + ";" + focusType + ";" + partialInteractiveRegion + ";"
+ + interactionId + ";" + callback + ";" + mFetchFlags + ";" + interrogatingPid
+ + ";" + interrogatingTid + ";" + spec);
+ }
try {
connection.getRemote().findFocus(accessibilityNodeId, focusType,
partialInteractiveRegion, interactionId, callback, mFetchFlags,
@@ -768,12 +794,12 @@
int direction, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".focusSearch",
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("focusSearch",
"accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
- + accessibilityNodeId + ";direction=" + direction + ";interactionId="
- + interactionId + ";callback=" + callback + ";interrogatingTid="
- + interrogatingTid);
+ + accessibilityNodeId + ";direction=" + direction + ";interactionId="
+ + interactionId + ";callback=" + callback + ";interrogatingTid="
+ + interrogatingTid);
}
final int resolvedWindowId;
RemoteAccessibilityConnection connection;
@@ -810,6 +836,12 @@
callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
interrogatingPid, interrogatingTid);
final long identityToken = Binder.clearCallingIdentity();
+ if (intConnTracingEnabled()) {
+ logTraceIntConn("focusSearch",
+ accessibilityNodeId + ";" + direction + ";" + partialInteractiveRegion
+ + ";" + interactionId + ";" + callback + ";" + mFetchFlags + ";"
+ + interrogatingPid + ";" + interrogatingTid + ";" + spec);
+ }
try {
connection.getRemote().focusSearch(accessibilityNodeId, direction,
partialInteractiveRegion, interactionId, callback, mFetchFlags,
@@ -832,17 +864,17 @@
@Override
public void sendGesture(int sequence, ParceledListSlice gestureSteps) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".sendGesture",
- "sequence=" + sequence + ";gestureSteps=" + gestureSteps);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn(
+ "sendGesture", "sequence=" + sequence + ";gestureSteps=" + gestureSteps);
}
}
@Override
public void dispatchGesture(int sequence, ParceledListSlice gestureSteps, int displayId) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".dispatchGesture", "sequence="
- + sequence + ";gestureSteps=" + gestureSteps + ";displayId=" + displayId);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("dispatchGesture", "sequence=" + sequence + ";gestureSteps="
+ + gestureSteps + ";displayId=" + displayId);
}
}
@@ -851,12 +883,12 @@
long accessibilityNodeId, int action, Bundle arguments, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".performAccessibilityAction",
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("performAccessibilityAction",
"accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
- + accessibilityNodeId + ";action=" + action + ";arguments=" + arguments
- + ";interactionId=" + interactionId + ";callback=" + callback
- + ";interrogatingTid=" + interrogatingTid);
+ + accessibilityNodeId + ";action=" + action + ";arguments=" + arguments
+ + ";interactionId=" + interactionId + ";callback=" + callback
+ + ";interrogatingTid=" + interrogatingTid);
}
final int resolvedWindowId;
synchronized (mLock) {
@@ -879,9 +911,8 @@
@Override
public boolean performGlobalAction(int action) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".performGlobalAction",
- "action=" + action);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("performGlobalAction", "action=" + action);
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
@@ -893,8 +924,8 @@
@Override
public @NonNull List<AccessibilityNodeInfo.AccessibilityAction> getSystemActions() {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getSystemActions");
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("getSystemActions", "");
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
@@ -906,9 +937,8 @@
@Override
public boolean isFingerprintGestureDetectionAvailable() {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(
- TRACE_A11Y_SERVICE_CONNECTION + ".isFingerprintGestureDetectionAvailable");
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("isFingerprintGestureDetectionAvailable", "");
}
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
return false;
@@ -923,9 +953,8 @@
@Override
public float getMagnificationScale(int displayId) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getMagnificationScale",
- "displayId=" + displayId);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("getMagnificationScale", "displayId=" + displayId);
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
@@ -942,9 +971,8 @@
@Override
public Region getMagnificationRegion(int displayId) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getMagnificationRegion",
- "displayId=" + displayId);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("getMagnificationRegion", "displayId=" + displayId);
}
synchronized (mLock) {
final Region region = Region.obtain();
@@ -970,9 +998,8 @@
@Override
public float getMagnificationCenterX(int displayId) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getMagnificationCenterX",
- "displayId=" + displayId);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("getMagnificationCenterX", "displayId=" + displayId);
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
@@ -996,9 +1023,8 @@
@Override
public float getMagnificationCenterY(int displayId) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getMagnificationCenterY",
- "displayId=" + displayId);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("getMagnificationCenterY", "displayId=" + displayId);
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
@@ -1032,9 +1058,8 @@
@Override
public boolean resetMagnification(int displayId, boolean animate) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".resetMagnification",
- "displayId=" + displayId + ";animate=" + animate);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("resetMagnification", "displayId=" + displayId + ";animate=" + animate);
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
@@ -1058,10 +1083,10 @@
@Override
public boolean setMagnificationScaleAndCenter(int displayId, float scale, float centerX,
float centerY, boolean animate) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setMagnificationScaleAndCenter",
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("setMagnificationScaleAndCenter",
"displayId=" + displayId + ";scale=" + scale + ";centerX=" + centerX
- + ";centerY=" + centerY + ";animate=" + animate);
+ + ";centerY=" + centerY + ";animate=" + animate);
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
@@ -1087,8 +1112,8 @@
@Override
public void setMagnificationCallbackEnabled(int displayId, boolean enabled) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setMagnificationCallbackEnabled",
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("setMagnificationCallbackEnabled",
"displayId=" + displayId + ";enabled=" + enabled);
}
mInvocationHandler.setMagnificationCallbackEnabled(displayId, enabled);
@@ -1100,18 +1125,16 @@
@Override
public void setSoftKeyboardCallbackEnabled(boolean enabled) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setSoftKeyboardCallbackEnabled",
- "enabled=" + enabled);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("setSoftKeyboardCallbackEnabled", "enabled=" + enabled);
}
mInvocationHandler.setSoftKeyboardCallbackEnabled(enabled);
}
@Override
public void takeScreenshot(int displayId, RemoteCallback callback) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".takeScreenshot",
- "displayId=" + displayId + ";callback=" + callback);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("takeScreenshot", "displayId=" + displayId + ";callback=" + callback);
}
final long currentTimestamp = SystemClock.uptimeMillis();
if (mRequestTakeScreenshotTimestampMs != 0
@@ -1237,6 +1260,10 @@
final long identity = Binder.clearCallingIdentity();
try {
final IBinder overlayWindowToken = new Binder();
+ if (wmTracingEnabled()) {
+ logTraceWM("addWindowToken",
+ overlayWindowToken + ";TYPE_ACCESSIBILITY_OVERLAY;" + displayId + ";null");
+ }
mWindowManagerService.addWindowToken(overlayWindowToken, TYPE_ACCESSIBILITY_OVERLAY,
displayId, null /* options */);
synchronized (mLock) {
@@ -1263,6 +1290,10 @@
*/
public void onDisplayRemoved(int displayId) {
final long identity = Binder.clearCallingIdentity();
+ if (wmTracingEnabled()) {
+ logTraceWM(
+ "addWindowToken", mOverlayWindowTokens.get(displayId) + ";true;" + displayId);
+ }
try {
mWindowManagerService.removeWindowToken(mOverlayWindowTokens.get(displayId), true,
displayId);
@@ -1282,9 +1313,8 @@
*/
@Override
public IBinder getOverlayWindowToken(int displayId) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getOverlayWindowToken",
- "displayId=" + displayId);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("getOverlayWindowToken", "displayId=" + displayId);
}
synchronized (mLock) {
return mOverlayWindowTokens.get(displayId);
@@ -1299,9 +1329,8 @@
*/
@Override
public int getWindowIdForLeashToken(@NonNull IBinder token) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getWindowIdForLeashToken",
- "token=" + token);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("getWindowIdForLeashToken", "token=" + token);
}
synchronized (mLock) {
return mA11yWindowManager.getWindowIdLocked(token);
@@ -1314,8 +1343,8 @@
// Clear the proxy in the other process so this
// IAccessibilityServiceConnection can be garbage collected.
if (mServiceInterface != null) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".init", "null, " + mId + ", null");
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("init", "null, " + mId + ", null");
}
mServiceInterface.init(null, mId, null);
}
@@ -1465,9 +1494,8 @@
}
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onAccessibilityEvent",
- event + ";" + serviceWantsEvent);
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("onAccessibilityEvent", event + ";" + serviceWantsEvent);
}
listener.onAccessibilityEvent(event, serviceWantsEvent);
if (DEBUG) {
@@ -1522,9 +1550,9 @@
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onMagnificationChanged", displayId
- + ", " + region + ", " + scale + ", " + centerX + ", " + centerY);
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("onMagnificationChanged", displayId + ", " + region + ", "
+ + scale + ", " + centerX + ", " + centerY);
}
listener.onMagnificationChanged(displayId, region, scale, centerX, centerY);
} catch (RemoteException re) {
@@ -1541,9 +1569,8 @@
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onSoftKeyboardShowModeChanged",
- String.valueOf(showState));
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("onSoftKeyboardShowModeChanged", String.valueOf(showState));
}
listener.onSoftKeyboardShowModeChanged(showState);
} catch (RemoteException re) {
@@ -1557,9 +1584,8 @@
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onAccessibilityButtonClicked",
- String.valueOf(displayId));
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("onAccessibilityButtonClicked", String.valueOf(displayId));
}
listener.onAccessibilityButtonClicked(displayId);
} catch (RemoteException re) {
@@ -1579,9 +1605,8 @@
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(
- TRACE_A11Y_SERVICE_CLIENT + ".onAccessibilityButtonAvailabilityChanged",
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("onAccessibilityButtonAvailabilityChanged",
String.valueOf(available));
}
listener.onAccessibilityButtonAvailabilityChanged(available);
@@ -1597,9 +1622,8 @@
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onGesture",
- gestureInfo.toString());
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("onGesture", gestureInfo.toString());
}
listener.onGesture(gestureInfo);
} catch (RemoteException re) {
@@ -1613,8 +1637,8 @@
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onSystemActionsChanged");
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("onSystemActionsChanged", "");
}
listener.onSystemActionsChanged();
} catch (RemoteException re) {
@@ -1628,8 +1652,8 @@
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".clearAccessibilityCache");
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("clearAccessibilityCache", "");
}
listener.clearAccessibilityCache();
} catch (RemoteException re) {
@@ -1747,6 +1771,12 @@
LocalServices.getService(ActivityTaskManagerInternal.class)
.setFocusedActivity(activityToken);
}
+ if (intConnTracingEnabled()) {
+ logTraceIntConn("performAccessibilityAction",
+ accessibilityNodeId + ";" + action + ";" + arguments + ";" + interactionId
+ + ";" + callback + ";" + mFetchFlags + ";" + interrogatingPid + ";"
+ + interrogatingTid);
+ }
connection.getRemote().performAccessibilityAction(accessibilityNodeId, action,
arguments, interactionId, callback, fetchFlags, interrogatingPid,
interrogatingTid);
@@ -1957,8 +1987,8 @@
@Override
public void setGestureDetectionPassthroughRegion(int displayId, Region region) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setGestureDetectionPassthroughRegion",
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("setGestureDetectionPassthroughRegion",
"displayId=" + displayId + ";region=" + region);
}
mSystemSupport.setGestureDetectionPassthroughRegion(displayId, region);
@@ -1966,8 +1996,8 @@
@Override
public void setTouchExplorationPassthroughRegion(int displayId, Region region) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setTouchExplorationPassthroughRegion",
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("setTouchExplorationPassthroughRegion",
"displayId=" + displayId + ";region=" + region);
}
mSystemSupport.setTouchExplorationPassthroughRegion(displayId, region);
@@ -1975,20 +2005,56 @@
@Override
public void setFocusAppearance(int strokeWidth, int color) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setFocusAppearance",
- "strokeWidth=" + strokeWidth + ";color=" + color);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("setFocusAppearance", "strokeWidth=" + strokeWidth + ";color=" + color);
}
}
@Override
- public void logTrace(long timestamp, String where, String callingParams, int processId,
- long threadId, int callingUid, Bundle callingStack) {
- if (mTrace.isA11yTracingEnabled()) {
+ public void logTrace(long timestamp, String where, long loggingTypes, String callingParams,
+ int processId, long threadId, int callingUid, Bundle callingStack) {
+ if (mTrace.isA11yTracingEnabledForTypes(loggingTypes)) {
ArrayList<StackTraceElement> list =
(ArrayList<StackTraceElement>) callingStack.getSerializable(CALL_STACK);
- mTrace.logTrace(timestamp, where, callingParams, processId, threadId, callingUid,
- list.toArray(new StackTraceElement[list.size()]));
+ HashSet<String> ignoreList =
+ (HashSet<String>) callingStack.getSerializable(IGNORE_CALL_STACK);
+ mTrace.logTrace(timestamp, where, loggingTypes, callingParams, processId, threadId,
+ callingUid, list.toArray(new StackTraceElement[list.size()]), ignoreList);
}
}
+
+ protected boolean svcClientTracingEnabled() {
+ return mTrace.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_SERVICE_CLIENT);
+ }
+
+ protected void logTraceSvcClient(String methodName, String params) {
+ mTrace.logTrace(TRACE_SVC_CLIENT + "." + methodName,
+ FLAGS_ACCESSIBILITY_SERVICE_CLIENT, params);
+ }
+
+ protected boolean svcConnTracingEnabled() {
+ return mTrace.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_SERVICE_CONNECTION);
+ }
+
+ protected void logTraceSvcConn(String methodName, String params) {
+ mTrace.logTrace(TRACE_SVC_CONN + "." + methodName,
+ FLAGS_ACCESSIBILITY_SERVICE_CONNECTION, params);
+ }
+
+ protected boolean intConnTracingEnabled() {
+ return mTrace.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION);
+ }
+
+ protected void logTraceIntConn(String methodName, String params) {
+ mTrace.logTrace(LOG_TAG + "." + methodName,
+ FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION, params);
+ }
+
+ protected boolean wmTracingEnabled() {
+ return mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL);
+ }
+
+ protected void logTraceWM(String methodName, String params) {
+ mTrace.logTrace(TRACE_WM + "." + methodName, FLAGS_WINDOW_MANAGER_INTERNAL, params);
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 7403af7..bae2e1a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -19,6 +19,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
+import android.accessibilityservice.AccessibilityTrace;
import android.annotation.MainThread;
import android.content.Context;
import android.graphics.Region;
@@ -224,7 +225,12 @@
Slog.d(TAG, "Received event: " + event + ", policyFlags=0x"
+ Integer.toHexString(policyFlags));
}
-
+ if (mAms.getTraceManager().isA11yTracingEnabledForTypes(
+ AccessibilityTrace.FLAGS_INPUT_FILTER)) {
+ mAms.getTraceManager().logTrace(TAG + ".onInputEvent",
+ AccessibilityTrace.FLAGS_INPUT_FILTER,
+ "event=" + event + ";policyFlags=" + policyFlags);
+ }
if (mEventHandler.size() == 0) {
if (DEBUG) Slog.d(TAG, "No mEventHandler for event " + event);
super.onInputEvent(event, policyFlags);
@@ -424,7 +430,8 @@
final ArrayList<Display> displaysList = mAms.getValidDisplayList();
if ((mEnabledFeatures & FLAG_FEATURE_AUTOCLICK) != 0) {
- mAutoclickController = new AutoclickController(mContext, mUserId);
+ mAutoclickController = new AutoclickController(
+ mContext, mUserId, mAms.getTraceManager());
addFirstEventHandlerForAllDisplays(displaysList, mAutoclickController);
}
@@ -462,7 +469,7 @@
if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) {
MotionEventInjector injector = new MotionEventInjector(
- mContext.getMainLooper());
+ mContext.getMainLooper(), mAms.getTraceManager());
addFirstEventHandler(displayId, injector);
mMotionEventInjectors.put(displayId, injector);
}
@@ -563,15 +570,15 @@
final Context uiContext = displayContext.createWindowContext(
TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, null /* options */);
magnificationGestureHandler = new WindowMagnificationGestureHandler(uiContext,
- mAms.getWindowMagnificationMgr(), mAms.getMagnificationController(),
- detectControlGestures, triggerable,
+ mAms.getWindowMagnificationMgr(), mAms.getTraceManager(),
+ mAms.getMagnificationController(), detectControlGestures, triggerable,
displayId);
} else {
final Context uiContext = displayContext.createWindowContext(
TYPE_MAGNIFICATION_OVERLAY, null /* options */);
magnificationGestureHandler = new FullScreenMagnificationGestureHandler(uiContext,
- mAms.getFullScreenMagnificationController(), mAms.getMagnificationController(),
- detectControlGestures, triggerable,
+ mAms.getFullScreenMagnificationController(), mAms.getTraceManager(),
+ mAms.getMagnificationController(), detectControlGestures, triggerable,
new WindowMagnificationPromptController(displayContext, mUserId), displayId);
}
return magnificationGestureHandler;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 7eecc45..ab464193 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -16,6 +16,15 @@
package com.android.server.accessibility;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_MANAGER;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_MANAGER_CLIENT;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_FINGERPRINT;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_INPUT_FILTER;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_PACKAGE_BROADCAST_RECEIVER;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_USER_BROADCAST_RECEIVER;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
import static android.provider.Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED;
import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
@@ -289,8 +298,8 @@
mContext = context;
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
- mTraceManager = new AccessibilityTraceManager(
- mWindowManagerService.getAccessibilityController(), this);
+ mTraceManager = AccessibilityTraceManager.getInstance(
+ mWindowManagerService.getAccessibilityController(), this, mLock);
mMainHandler = new MainHandler(mContext.getMainLooper());
mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class);
mPackageManager = packageManager;
@@ -311,8 +320,8 @@
mContext = context;
mPowerManager = context.getSystemService(PowerManager.class);
mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
- mTraceManager = new AccessibilityTraceManager(
- mWindowManagerService.getAccessibilityController(), this);
+ mTraceManager = AccessibilityTraceManager.getInstance(
+ mWindowManagerService.getAccessibilityController(), this, mLock);
mMainHandler = new MainHandler(mContext.getMainLooper());
mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class);
mPackageManager = mContext.getPackageManager();
@@ -324,7 +333,7 @@
mSecurityPolicy = new AccessibilitySecurityPolicy(policyWarningUIController, mContext,
this);
mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler,
- mWindowManagerService, this, mSecurityPolicy, this);
+ mWindowManagerService, this, mSecurityPolicy, this, mTraceManager);
mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
mMagnificationController = new MagnificationController(this, mLock, mContext);
init();
@@ -339,26 +348,16 @@
@Override
public int getCurrentUserIdLocked() {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".getCurrentUserIdLocked");
- }
return mCurrentUserId;
}
@Override
public boolean isAccessibilityButtonShown() {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".isAccessibilityButtonShown");
- }
return mIsAccessibilityButtonShown;
}
@Override
public void onServiceInfoChangedLocked(AccessibilityUserState userState) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(
- LOG_TAG + ".onServiceInfoChangedLocked", "userState=" + userState);
- }
mSecurityPolicy.onBoundServicesChangedLocked(userState.mUserId,
userState.mBoundServices);
scheduleNotifyClientsOfServicesStateChangeLocked(userState);
@@ -424,8 +423,9 @@
PackageMonitor monitor = new PackageMonitor() {
@Override
public void onSomePackagesChanged() {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".PM.onSomePackagesChanged");
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_PACKAGE_BROADCAST_RECEIVER)) {
+ mTraceManager.logTrace(LOG_TAG + ".PM.onSomePackagesChanged",
+ FLAGS_PACKAGE_BROADCAST_RECEIVER);
}
synchronized (mLock) {
@@ -452,8 +452,9 @@
// mBindingServices in binderDied() during updating. Remove services from this
// package from mBindingServices, and then update the user state to re-bind new
// versions of them.
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_PACKAGE_BROADCAST_RECEIVER)) {
mTraceManager.logTrace(LOG_TAG + ".PM.onPackageUpdateFinished",
+ FLAGS_PACKAGE_BROADCAST_RECEIVER,
"packageName=" + packageName + ";uid=" + uid);
}
synchronized (mLock) {
@@ -485,8 +486,9 @@
@Override
public void onPackageRemoved(String packageName, int uid) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_PACKAGE_BROADCAST_RECEIVER)) {
mTraceManager.logTrace(LOG_TAG + ".PM.onPackageRemoved",
+ FLAGS_PACKAGE_BROADCAST_RECEIVER,
"packageName=" + packageName + ";uid=" + uid);
}
@@ -529,8 +531,9 @@
@Override
public boolean onHandleForceStop(Intent intent, String[] packages,
int uid, boolean doit) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_PACKAGE_BROADCAST_RECEIVER)) {
mTraceManager.logTrace(LOG_TAG + ".PM.onHandleForceStop",
+ FLAGS_PACKAGE_BROADCAST_RECEIVER,
"intent=" + intent + ";packages=" + packages + ";uid=" + uid
+ ";doit=" + doit);
}
@@ -580,8 +583,8 @@
mContext.registerReceiverAsUser(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".BR.onReceive",
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_USER_BROADCAST_RECEIVER)) {
+ mTraceManager.logTrace(LOG_TAG + ".BR.onReceive", FLAGS_USER_BROADCAST_RECEIVER,
"context=" + context + ";intent=" + intent);
}
@@ -668,8 +671,8 @@
@Override
public long addClient(IAccessibilityManagerClient callback, int userId) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".addClient",
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+ mTraceManager.logTrace(LOG_TAG + ".addClient", FLAGS_ACCESSIBILITY_MANAGER,
"callback=" + callback + ";userId=" + userId);
}
@@ -739,11 +742,12 @@
@Override
public void sendAccessibilityEvent(AccessibilityEvent event, int userId) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".sendAccessibilityEvent",
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+ mTraceManager.logTrace(LOG_TAG + ".sendAccessibilityEvent", FLAGS_ACCESSIBILITY_MANAGER,
"event=" + event + ";userId=" + userId);
}
boolean dispatchEvent = false;
+ int resolvedUserId;
synchronized (mLock) {
if (event.getWindowId() ==
@@ -759,8 +763,7 @@
// We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below
// performs the current profile parent resolution.
- final int resolvedUserId = mSecurityPolicy
- .resolveCallingUserIdEnforcingPermissionsLocked(userId);
+ resolvedUserId = mSecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked(userId);
// Make sure the reported package is one the caller has access to.
event.setPackageName(mSecurityPolicy.resolveValidReportedPackageLocked(
@@ -792,17 +795,23 @@
int displayId = Display.INVALID_DISPLAY;
synchronized (mLock) {
final int windowId = event.getWindowId();
- if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
- && windowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) {
+ if (windowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) {
displayId = mA11yWindowManager.getDisplayIdByUserIdAndWindowIdLocked(
- mCurrentUserId, windowId);
+ resolvedUserId, windowId);
+ event.setDisplayId(displayId);
}
- if (displayId != Display.INVALID_DISPLAY
+
+ if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
+ && displayId != Display.INVALID_DISPLAY
&& mA11yWindowManager.isTrackingWindowsLocked(displayId)) {
shouldComputeWindows = true;
}
}
if (shouldComputeWindows) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL)) {
+ mTraceManager.logTrace("WindowManagerInternal.computeWindowsForAccessibility",
+ FLAGS_WINDOW_MANAGER_INTERNAL, "display=" + displayId);
+ }
final WindowManagerInternal wm = LocalServices.getService(
WindowManagerInternal.class);
wm.computeWindowsForAccessibility(displayId);
@@ -835,9 +844,9 @@
*/
@Override
public void registerSystemAction(RemoteAction action, int actionId) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".registerSystemAction",
- "action=" + action + ";actionId=" + actionId);
+ FLAGS_ACCESSIBILITY_MANAGER, "action=" + action + ";actionId=" + actionId);
}
mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
getSystemActionPerformer().registerSystemAction(actionId, action);
@@ -850,8 +859,9 @@
*/
@Override
public void unregisterSystemAction(int actionId) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".unregisterSystemAction", "actionId=" + actionId);
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+ mTraceManager.logTrace(LOG_TAG + ".unregisterSystemAction",
+ FLAGS_ACCESSIBILITY_MANAGER, "actionId=" + actionId);
}
mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
getSystemActionPerformer().unregisterSystemAction(actionId);
@@ -867,9 +877,9 @@
@Override
public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(int userId) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".getInstalledAccessibilityServiceList",
- "userId=" + userId);
+ FLAGS_ACCESSIBILITY_MANAGER, "userId=" + userId);
}
synchronized (mLock) {
@@ -889,8 +899,9 @@
@Override
public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType,
int userId) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".getEnabledAccessibilityServiceList",
+ FLAGS_ACCESSIBILITY_MANAGER,
"feedbackType=" + feedbackType + ";userId=" + userId);
}
@@ -922,8 +933,9 @@
@Override
public void interrupt(int userId) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".interrupt", "userId=" + userId);
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+ mTraceManager.logTrace(LOG_TAG + ".interrupt",
+ FLAGS_ACCESSIBILITY_MANAGER, "userId=" + userId);
}
List<IAccessibilityServiceClient> interfacesToInterrupt;
@@ -952,8 +964,10 @@
}
for (int i = 0, count = interfacesToInterrupt.size(); i < count; i++) {
try {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".IAccessibilityServiceClient.onInterrupt");
+ if (mTraceManager.isA11yTracingEnabledForTypes(
+ FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) {
+ mTraceManager.logTrace(LOG_TAG + ".IAccessibilityServiceClient.onInterrupt",
+ FLAGS_ACCESSIBILITY_SERVICE_CLIENT);
}
interfacesToInterrupt.get(i).onInterrupt();
} catch (RemoteException re) {
@@ -967,8 +981,9 @@
public int addAccessibilityInteractionConnection(IWindow windowToken, IBinder leashToken,
IAccessibilityInteractionConnection connection, String packageName,
int userId) throws RemoteException {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".addAccessibilityInteractionConnection",
+ FLAGS_ACCESSIBILITY_MANAGER,
"windowToken=" + windowToken + "leashToken=" + leashToken + ";connection="
+ connection + "; packageName=" + packageName + ";userId=" + userId);
}
@@ -979,9 +994,9 @@
@Override
public void removeAccessibilityInteractionConnection(IWindow window) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".removeAccessibilityInteractionConnection",
- "window=" + window);
+ FLAGS_ACCESSIBILITY_MANAGER, "window=" + window);
}
mA11yWindowManager.removeAccessibilityInteractionConnection(window);
}
@@ -989,9 +1004,9 @@
@Override
public void setPictureInPictureActionReplacingConnection(
IAccessibilityInteractionConnection connection) throws RemoteException {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".setPictureInPictureActionReplacingConnection",
- "connection=" + connection);
+ FLAGS_ACCESSIBILITY_MANAGER, "connection=" + connection);
}
mSecurityPolicy.enforceCallingPermission(Manifest.permission.MODIFY_ACCESSIBILITY_DATA,
SET_PIP_ACTION_REPLACEMENT);
@@ -1003,10 +1018,11 @@
IAccessibilityServiceClient serviceClient,
AccessibilityServiceInfo accessibilityServiceInfo,
int flags) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".registerUiTestAutomationService", "owner=" + owner
- + ";serviceClient=" + serviceClient + ";accessibilityServiceInfo="
- + accessibilityServiceInfo + ";flags=" + flags);
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+ mTraceManager.logTrace(LOG_TAG + ".registerUiTestAutomationService",
+ FLAGS_ACCESSIBILITY_MANAGER,
+ "owner=" + owner + ";serviceClient=" + serviceClient
+ + ";accessibilityServiceInfo=" + accessibilityServiceInfo + ";flags=" + flags);
}
mSecurityPolicy.enforceCallingPermission(Manifest.permission.RETRIEVE_WINDOW_CONTENT,
@@ -1023,9 +1039,9 @@
@Override
public void unregisterUiTestAutomationService(IAccessibilityServiceClient serviceClient) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".unregisterUiTestAutomationService",
- "serviceClient=" + serviceClient);
+ FLAGS_ACCESSIBILITY_MANAGER, "serviceClient=" + serviceClient);
}
synchronized (mLock) {
mUiAutomationManager.unregisterUiTestAutomationServiceLocked(serviceClient);
@@ -1035,15 +1051,20 @@
@Override
public void temporaryEnableAccessibilityStateUntilKeyguardRemoved(
ComponentName service, boolean touchExplorationEnabled) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(
LOG_TAG + ".temporaryEnableAccessibilityStateUntilKeyguardRemoved",
+ FLAGS_ACCESSIBILITY_MANAGER,
"service=" + service + ";touchExplorationEnabled=" + touchExplorationEnabled);
}
mSecurityPolicy.enforceCallingPermission(
Manifest.permission.TEMPORARY_ENABLE_ACCESSIBILITY,
TEMPORARY_ENABLE_ACCESSIBILITY_UNTIL_KEYGUARD_REMOVED);
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL)) {
+ mTraceManager.logTrace("WindowManagerInternal.isKeyguardLocked",
+ FLAGS_WINDOW_MANAGER_INTERNAL);
+ }
if (!mWindowManagerService.isKeyguardLocked()) {
return;
}
@@ -1069,9 +1090,9 @@
@Override
public IBinder getWindowToken(int windowId, int userId) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".getWindowToken",
- "windowId=" + windowId + ";userId=" + userId);
+ FLAGS_ACCESSIBILITY_MANAGER, "windowId=" + windowId + ";userId=" + userId);
}
mSecurityPolicy.enforceCallingPermission(
@@ -1113,8 +1134,9 @@
*/
@Override
public void notifyAccessibilityButtonClicked(int displayId, String targetName) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".notifyAccessibilityButtonClicked",
+ FLAGS_ACCESSIBILITY_MANAGER,
"displayId=" + displayId + ";targetName=" + targetName);
}
@@ -1143,9 +1165,9 @@
*/
@Override
public void notifyAccessibilityButtonVisibilityChanged(boolean shown) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".notifyAccessibilityButtonVisibilityChanged",
- "shown=" + shown);
+ FLAGS_ACCESSIBILITY_MANAGER, "shown=" + shown);
}
mSecurityPolicy.enforceCallingOrSelfPermission(
@@ -1176,10 +1198,6 @@
*/
@Override
public void onSystemActionsChanged() {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".onSystemActionsChanged");
- }
-
synchronized (mLock) {
AccessibilityUserState state = getCurrentUserStateLocked();
notifySystemActionsChangedLocked(state);
@@ -1242,11 +1260,6 @@
@Override
public @Nullable MotionEventInjector getMotionEventInjectorForDisplayLocked(int displayId) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".getMotionEventInjectorForDisplayLocked",
- "displayId=" + displayId);
- }
-
final long endMillis = SystemClock.uptimeMillis() + WAIT_MOTION_INJECTOR_TIMEOUT_MILLIS;
MotionEventInjector motionEventInjector = null;
while ((mMotionEventInjectors == null) && (SystemClock.uptimeMillis() < endMillis)) {
@@ -1309,6 +1322,10 @@
synchronized (mLock) {
token = getWindowToken(windowId, mCurrentUserId);
}
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL)) {
+ mTraceManager.logTrace("WindowManagerInternal.getWindowFrame",
+ FLAGS_WINDOW_MANAGER_INTERNAL, "token=" + token + ";outBounds=" + outBounds);
+ }
mWindowManagerService.getWindowFrame(token, outBounds);
if (!outBounds.isEmpty()) {
return true;
@@ -1457,7 +1474,7 @@
private int getClientStateLocked(AccessibilityUserState userState) {
return userState.getClientStateLocked(
mUiAutomationManager.isUiAutomationRunningLocked(),
- mTraceManager.isA11yTracingEnabled());
+ mTraceManager.getTraceStateForAccessibilityManagerClientState());
}
private InteractionBridge getInteractionBridge() {
@@ -1667,6 +1684,10 @@
}
private void updateRelevantEventsLocked(AccessibilityUserState userState) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) {
+ mTraceManager.logTrace(LOG_TAG + ".updateRelevantEventsLocked",
+ FLAGS_ACCESSIBILITY_SERVICE_CLIENT, "userState=" + userState);
+ }
mMainHandler.post(() -> {
broadcastToClients(userState, ignoreRemoteException(client -> {
int relevantEventTypes;
@@ -1816,12 +1837,6 @@
@Override
public void persistComponentNamesToSettingLocked(String settingName,
Set<ComponentName> componentNames, int userId) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".persistComponentNamesToSettingLocked",
- "settingName=" + settingName + ";componentNames=" + componentNames + ";userId="
- + userId);
- }
-
persistColonDelimitedSetToSettingLocked(settingName, userId, componentNames,
componentName -> componentName.flattenToShortString());
}
@@ -1946,7 +1961,7 @@
}
}
- private void scheduleUpdateClientsIfNeededLocked(AccessibilityUserState userState) {
+ void scheduleUpdateClientsIfNeededLocked(AccessibilityUserState userState) {
final int clientState = getClientStateLocked(userState);
if (userState.getLastSentClientStateLocked() != clientState
&& (mGlobalClients.getRegisteredCallbackCount() > 0
@@ -1969,6 +1984,10 @@
private void sendStateToClients(int clientState,
RemoteCallbackList<IAccessibilityManagerClient> clients) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER_CLIENT)) {
+ mTraceManager.logTrace(LOG_TAG + ".sendStateToClients",
+ FLAGS_ACCESSIBILITY_MANAGER_CLIENT, "clientState=" + clientState);
+ }
clients.broadcast(ignoreRemoteException(
client -> client.setState(clientState)));
}
@@ -1989,6 +2008,10 @@
private void notifyClientsOfServicesStateChange(
RemoteCallbackList<IAccessibilityManagerClient> clients, long uiTimeout) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER_CLIENT)) {
+ mTraceManager.logTrace(LOG_TAG + ".notifyClientsOfServicesStateChange",
+ FLAGS_ACCESSIBILITY_MANAGER_CLIENT, "uiTimeout=" + uiTimeout);
+ }
clients.broadcast(ignoreRemoteException(
client -> client.notifyServicesStateChanged(uiTimeout)));
}
@@ -2068,6 +2091,12 @@
}
}
if (setInputFilter) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL
+ | FLAGS_INPUT_FILTER)) {
+ mTraceManager.logTrace("WindowManagerInternal.setInputFilter",
+ FLAGS_WINDOW_MANAGER_INTERNAL | FLAGS_INPUT_FILTER,
+ "inputFilter=" + inputFilter);
+ }
mWindowManagerService.setInputFilter(inputFilter);
}
}
@@ -2791,26 +2820,21 @@
@GuardedBy("mLock")
@Override
public MagnificationSpec getCompatibleMagnificationSpecLocked(int windowId) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".getCompatibleMagnificationSpecLocked",
- "windowId=" + windowId);
- }
-
IBinder windowToken = mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked(
mCurrentUserId, windowId);
if (windowToken != null) {
- return mWindowManagerService.getCompatibleMagnificationSpecForWindow(
- windowToken);
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL)) {
+ mTraceManager.logTrace(LOG_TAG + ".getCompatibleMagnificationSpecForWindow",
+ FLAGS_WINDOW_MANAGER_INTERNAL, "windowToken=" + windowToken);
+ }
+
+ return mWindowManagerService.getCompatibleMagnificationSpecForWindow(windowToken);
}
return null;
}
@Override
public KeyEventDispatcher getKeyEventDispatcher() {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".getKeyEventDispatcher");
- }
-
if (mKeyEventDispatcher == null) {
mKeyEventDispatcher = new KeyEventDispatcher(
mMainHandler, MainHandler.MSG_SEND_KEY_EVENT_TO_INPUT_FILTER, mLock,
@@ -2823,13 +2847,6 @@
@SuppressWarnings("AndroidFrameworkPendingIntentMutability")
public PendingIntent getPendingIntentActivity(Context context, int requestCode, Intent intent,
int flags) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".getPendingIntentActivity",
- "context=" + context + ";requestCode=" + requestCode + ";intent=" + intent
- + ";flags=" + flags);
- }
-
-
return PendingIntent.getActivity(context, requestCode, intent, flags);
}
@@ -2844,9 +2861,9 @@
*/
@Override
public void performAccessibilityShortcut(String targetName) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".performAccessibilityShortcut",
- "targetName=" + targetName);
+ FLAGS_ACCESSIBILITY_MANAGER, "targetName=" + targetName);
}
if ((UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID)
@@ -3033,9 +3050,9 @@
@Override
public List<String> getAccessibilityShortcutTargets(@ShortcutType int shortcutType) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".getAccessibilityShortcutTargets",
- "shortcutType=" + shortcutType);
+ FLAGS_ACCESSIBILITY_MANAGER, "shortcutType=" + shortcutType);
}
if (mContext.checkCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
@@ -3107,11 +3124,6 @@
@Override
public void sendAccessibilityEventForCurrentUserLocked(AccessibilityEvent event) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".sendAccessibilityEventForCurrentUserLocked",
- "event=" + event);
- }
-
sendAccessibilityEventLocked(event, mCurrentUserId);
}
@@ -3133,8 +3145,10 @@
*/
@Override
public boolean sendFingerprintGesture(int gestureKeyCode) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(
+ FLAGS_ACCESSIBILITY_MANAGER | FLAGS_FINGERPRINT)) {
mTraceManager.logTrace(LOG_TAG + ".sendFingerprintGesture",
+ FLAGS_ACCESSIBILITY_MANAGER | FLAGS_FINGERPRINT,
"gestureKeyCode=" + gestureKeyCode);
}
@@ -3159,9 +3173,9 @@
*/
@Override
public int getAccessibilityWindowId(@Nullable IBinder windowToken) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".getAccessibilityWindowId",
- "windowToken=" + windowToken);
+ FLAGS_ACCESSIBILITY_MANAGER, "windowToken=" + windowToken);
}
synchronized (mLock) {
@@ -3181,8 +3195,9 @@
*/
@Override
public long getRecommendedTimeoutMillis() {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".getRecommendedTimeoutMillis");
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+ mTraceManager.logTrace(
+ LOG_TAG + ".getRecommendedTimeoutMillis", FLAGS_ACCESSIBILITY_MANAGER);
}
synchronized(mLock) {
@@ -3199,8 +3214,10 @@
@Override
public void setWindowMagnificationConnection(
IWindowMagnificationConnection connection) throws RemoteException {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(
+ FLAGS_ACCESSIBILITY_MANAGER | FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
mTraceManager.logTrace(LOG_TAG + ".setWindowMagnificationConnection",
+ FLAGS_ACCESSIBILITY_MANAGER | FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
"connection=" + connection);
}
@@ -3234,9 +3251,9 @@
@Override
public void associateEmbeddedHierarchy(@NonNull IBinder host, @NonNull IBinder embedded) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".associateEmbeddedHierarchy",
- "host=" + host + ";embedded=" + embedded);
+ FLAGS_ACCESSIBILITY_MANAGER, "host=" + host + ";embedded=" + embedded);
}
synchronized (mLock) {
@@ -3246,8 +3263,9 @@
@Override
public void disassociateEmbeddedHierarchy(@NonNull IBinder token) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".disassociateEmbeddedHierarchy", "token=" + token);
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+ mTraceManager.logTrace(LOG_TAG + ".disassociateEmbeddedHierarchy",
+ FLAGS_ACCESSIBILITY_MANAGER, "token=" + token);
}
synchronized (mLock) {
@@ -3259,7 +3277,11 @@
* Gets the stroke width of the focus rectangle.
* @return The stroke width.
*/
+ @Override
public int getFocusStrokeWidth() {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+ mTraceManager.logTrace(LOG_TAG + ".getFocusStrokeWidth", FLAGS_ACCESSIBILITY_MANAGER);
+ }
synchronized (mLock) {
final AccessibilityUserState userState = getCurrentUserStateLocked();
@@ -3271,7 +3293,11 @@
* Gets the color of the focus rectangle.
* @return The color.
*/
+ @Override
public int getFocusColor() {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+ mTraceManager.logTrace(LOG_TAG + ".getFocusColor", FLAGS_ACCESSIBILITY_MANAGER);
+ }
synchronized (mLock) {
final AccessibilityUserState userState = getCurrentUserStateLocked();
@@ -3335,9 +3361,6 @@
@Override
public FullScreenMagnificationController getFullScreenMagnificationController() {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".getFullScreenMagnificationController");
- }
synchronized (mLock) {
return mMagnificationController.getFullScreenMagnificationController();
}
@@ -3345,11 +3368,6 @@
@Override
public void onClientChangeLocked(boolean serviceInfoChanged) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".onClientChangeLocked",
- "serviceInfoChanged=" + serviceInfoChanged);
- }
-
AccessibilityUserState userState = getUserStateLocked(mCurrentUserId);
onUserStateChangedLocked(userState);
if (serviceInfoChanged) {
@@ -3876,11 +3894,6 @@
@Override
public void setGestureDetectionPassthroughRegion(int displayId, Region region) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".setGestureDetectionPassthroughRegion",
- "displayId=" + displayId + ";region=" + region);
- }
-
mMainHandler.sendMessage(
obtainMessage(
AccessibilityManagerService::setGestureDetectionPassthroughRegionInternal,
@@ -3891,11 +3904,6 @@
@Override
public void setTouchExplorationPassthroughRegion(int displayId, Region region) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".setTouchExplorationPassthroughRegion",
- "displayId=" + displayId + ";region=" + region);
- }
-
mMainHandler.sendMessage(
obtainMessage(
AccessibilityManagerService::setTouchExplorationPassthroughRegionInternal,
@@ -3924,7 +3932,10 @@
if (userState.mUserId != mCurrentUserId) {
return;
}
-
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) {
+ mTraceManager.logTrace(LOG_TAG + ".updateFocusAppearanceDataLocked",
+ FLAGS_ACCESSIBILITY_SERVICE_CLIENT, "userState=" + userState);
+ }
mMainHandler.post(() -> {
broadcastToClients(userState, ignoreRemoteException(client -> {
client.mCallback.setFocusAppearance(userState.getFocusStrokeWidthLocked(),
@@ -3934,7 +3945,7 @@
}
- AccessibilityTraceManager getTraceManager() {
+ public AccessibilityTraceManager getTraceManager() {
return mTraceManager;
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
index dc2628f..119d26e 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
@@ -559,17 +559,18 @@
return true;
}
- final int uid = resolveInfo.serviceInfo.applicationInfo.uid;
+ final int servicePackageUid = resolveInfo.serviceInfo.applicationInfo.uid;
+ final int callingPid = Binder.getCallingPid();
final long identityToken = Binder.clearCallingIdentity();
try {
// For the caller is system, just block the data to a11y services.
- if (OWN_PROCESS_ID == Binder.getCallingPid()) {
+ if (OWN_PROCESS_ID == callingPid) {
return mAppOpsManager.noteOpNoThrow(AppOpsManager.OPSTR_ACCESS_ACCESSIBILITY,
- uid, packageName) == AppOpsManager.MODE_ALLOWED;
+ servicePackageUid, packageName, null, null) == AppOpsManager.MODE_ALLOWED;
}
return mAppOpsManager.noteOp(AppOpsManager.OPSTR_ACCESS_ACCESSIBILITY,
- uid, packageName) == AppOpsManager.MODE_ALLOWED;
+ servicePackageUid, packageName, null, null) == AppOpsManager.MODE_ALLOWED;
} finally {
Binder.restoreCallingIdentity(identityToken);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 7d75b73..467cab5 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -20,6 +20,7 @@
import android.Manifest;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.app.PendingIntent;
import android.content.ComponentName;
@@ -53,10 +54,7 @@
*/
class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnection {
private static final String LOG_TAG = "AccessibilityServiceConnection";
- private static final String TRACE_A11Y_SERVICE_CONNECTION =
- LOG_TAG + ".IAccessibilityServiceConnection";
- private static final String TRACE_A11Y_SERVICE_CLIENT =
- LOG_TAG + ".IAccessibilityServiceClient";
+
/*
Holding a weak reference so there isn't a loop of references. AccessibilityUserState keeps
lists of bound and binding services. These are freed on user changes, but just in case it
@@ -137,8 +135,8 @@
@Override
public void disableSelf() {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".disableSelf");
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("disableSelf", "");
}
synchronized (mLock) {
AccessibilityUserState userState = mUserStateWeakReference.get();
@@ -218,9 +216,9 @@
return;
}
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".init", this + ", " + mId + ", "
- + mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("init",
+ this + "," + mId + "," + mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
}
serviceInterface.init(this, mId, mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
} catch (RemoteException re) {
@@ -264,9 +262,8 @@
@Override
public boolean setSoftKeyboardShowMode(int showMode) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setSoftKeyboardShowMode",
- "showMode=" + showMode);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("setSoftKeyboardShowMode", "showMode=" + showMode);
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
@@ -280,8 +277,8 @@
@Override
public int getSoftKeyboardShowMode() {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getSoftKeyboardShowMode");
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("getSoftKeyboardShowMode", "");
}
final AccessibilityUserState userState = mUserStateWeakReference.get();
return (userState != null) ? userState.getSoftKeyboardShowModeLocked() : 0;
@@ -289,9 +286,8 @@
@Override
public boolean switchToInputMethod(String imeId) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".switchToInputMethod",
- "imeId=" + imeId);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("switchToInputMethod", "imeId=" + imeId);
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
@@ -311,8 +307,8 @@
@Override
public boolean isAccessibilityButtonAvailable() {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".isAccessibilityButtonAvailable");
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("isAccessibilityButtonAvailable", "");
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
@@ -373,9 +369,9 @@
}
if (serviceInterface != null) {
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT
- + ".onFingerprintCapturingGesturesChanged", String.valueOf(active));
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient(
+ "onFingerprintCapturingGesturesChanged", String.valueOf(active));
}
mServiceInterface.onFingerprintCapturingGesturesChanged(active);
} catch (RemoteException e) {
@@ -394,9 +390,8 @@
}
if (serviceInterface != null) {
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onFingerprintGesture",
- String.valueOf(gesture));
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("onFingerprintGesture", String.valueOf(gesture));
}
mServiceInterface.onFingerprintGesture(gesture);
} catch (RemoteException e) {
@@ -410,15 +405,17 @@
if (mSecurityPolicy.canPerformGestures(this)) {
MotionEventInjector motionEventInjector =
mSystemSupport.getMotionEventInjectorForDisplayLocked(displayId);
+ if (wmTracingEnabled()) {
+ logTraceWM("isTouchOrFaketouchDevice", "");
+ }
if (motionEventInjector != null
&& mWindowManagerService.isTouchOrFaketouchDevice()) {
motionEventInjector.injectEvents(
gestureSteps.getList(), mServiceInterface, sequence, displayId);
} else {
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onPerformGestureResult",
- sequence + ", false");
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("onPerformGestureResult", sequence + ", false");
}
mServiceInterface.onPerformGestureResult(sequence, false);
} catch (RemoteException re) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java
index 6396960..8cf5547 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java
@@ -60,7 +60,7 @@
}
case "start-trace":
case "stop-trace":
- return mService.getTraceManager().onShellCommand(cmd);
+ return mService.getTraceManager().onShellCommand(cmd, this);
}
return -1;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityTrace.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityTrace.java
deleted file mode 100644
index 0391413..0000000
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityTrace.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/**
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.accessibility;
-
-/**
- * Interface to log accessibility trace.
- */
-public interface AccessibilityTrace {
- /**
- * Whether the trace is enabled.
- */
- boolean isA11yTracingEnabled();
-
- /**
- * Start tracing.
- */
- void startTrace();
-
- /**
- * Stop tracing.
- */
- void stopTrace();
-
- /**
- * Log one trace entry.
- * @param where A string to identify this log entry, which can be used to filter/search
- * through the tracing file.
- */
- void logTrace(String where);
-
- /**
- * Log one trace entry.
- * @param where A string to identify this log entry, which can be used to filter/search
- * through the tracing file.
- * @param callingParams The parameters for the method to be logged.
- */
- void logTrace(String where, String callingParams);
-
- /**
- * Log one trace entry. Accessibility services using AccessibilityInteractionClient to
- * make screen content related requests use this API to log entry when receive callback.
- * @param timestamp The timestamp when a callback is received.
- * @param where A string to identify this log entry, which can be used to filter/search
- * through the tracing file.
- * @param callingParams The parameters for the callback.
- * @param processId The process id of the calling component.
- * @param threadId The threadId of the calling component.
- * @param callingUid The calling uid of the callback.
- * @param callStack The call stack of the callback.
- */
- void logTrace(long timestamp, String where, String callingParams, int processId,
- long threadId, int callingUid, StackTraceElement[] callStack);
-}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityTraceManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityTraceManager.java
index 6105e8a..51e01ea 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityTraceManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityTraceManager.java
@@ -15,72 +15,197 @@
*/
package com.android.server.accessibility;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CLIENT;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_MANAGER_CLIENT_STATES;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_LOGGING_ALL;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_LOGGING_NONE;
+import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TRACE_A11Y_INTERACTION_CLIENT_ENABLED;
+import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_CB_ENABLED;
+import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_ENABLED;
+import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TRACE_A11Y_SERVICE_ENABLED;
+
+import android.accessibilityservice.AccessibilityTrace;
+import android.annotation.MainThread;
import android.annotation.NonNull;
import android.os.Binder;
+import android.os.ShellCommand;
import com.android.server.wm.WindowManagerInternal;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
/**
* Manager of accessibility trace.
*/
-class AccessibilityTraceManager implements AccessibilityTrace {
+public class AccessibilityTraceManager implements AccessibilityTrace {
private final WindowManagerInternal.AccessibilityControllerInternal mA11yController;
private final AccessibilityManagerService mService;
+ private final Object mA11yMSLock;
- AccessibilityTraceManager(
+ private long mEnabledLoggingFlags;
+
+ private static AccessibilityTraceManager sInstance = null;
+
+ @MainThread
+ static AccessibilityTraceManager getInstance(
@NonNull WindowManagerInternal.AccessibilityControllerInternal a11yController,
- @NonNull AccessibilityManagerService service) {
+ @NonNull AccessibilityManagerService service,
+ @NonNull Object lock) {
+ if (sInstance == null) {
+ sInstance = new AccessibilityTraceManager(a11yController, service, lock);
+ }
+ return sInstance;
+ }
+
+ private AccessibilityTraceManager(
+ @NonNull WindowManagerInternal.AccessibilityControllerInternal a11yController,
+ @NonNull AccessibilityManagerService service,
+ @NonNull Object lock) {
mA11yController = a11yController;
mService = service;
+ mA11yMSLock = lock;
+ mEnabledLoggingFlags = FLAGS_LOGGING_NONE;
}
@Override
public boolean isA11yTracingEnabled() {
- return mA11yController.isAccessibilityTracingEnabled();
+ synchronized (mA11yMSLock) {
+ return mEnabledLoggingFlags != FLAGS_LOGGING_NONE;
+ }
}
@Override
- public void startTrace() {
- if (!mA11yController.isAccessibilityTracingEnabled()) {
- mA11yController.startTrace();
- mService.scheduleUpdateClientsIfNeeded(mService.getCurrentUserState());
+ public boolean isA11yTracingEnabledForTypes(long typeIdFlags) {
+ synchronized (mA11yMSLock) {
+ return ((typeIdFlags & mEnabledLoggingFlags) != FLAGS_LOGGING_NONE);
}
}
@Override
+ public int getTraceStateForAccessibilityManagerClientState() {
+ int state = 0x0;
+ synchronized (mA11yMSLock) {
+ if (isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION)) {
+ state |= STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_ENABLED;
+ }
+ if (isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK)) {
+ state |= STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_CB_ENABLED;
+ }
+ if (isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_INTERACTION_CLIENT)) {
+ state |= STATE_FLAG_TRACE_A11Y_INTERACTION_CLIENT_ENABLED;
+ }
+ if (isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_SERVICE)) {
+ state |= STATE_FLAG_TRACE_A11Y_SERVICE_ENABLED;
+ }
+ }
+ return state;
+ }
+
+ @Override
+ public void startTrace(long loggingTypes) {
+ if (loggingTypes == FLAGS_LOGGING_NONE) {
+ // Ignore start none request
+ return;
+ }
+
+ synchronized (mA11yMSLock) {
+ long oldEnabled = mEnabledLoggingFlags;
+ mEnabledLoggingFlags = loggingTypes;
+
+ if (needToNotifyClients(oldEnabled)) {
+ mService.scheduleUpdateClientsIfNeededLocked(mService.getCurrentUserState());
+ }
+ }
+
+ mA11yController.startTrace(loggingTypes);
+ }
+
+ @Override
public void stopTrace() {
- if (mA11yController.isAccessibilityTracingEnabled()) {
+ boolean stop = false;
+ synchronized (mA11yMSLock) {
+ stop = isA11yTracingEnabled();
+
+ long oldEnabled = mEnabledLoggingFlags;
+ mEnabledLoggingFlags = FLAGS_LOGGING_NONE;
+
+ if (needToNotifyClients(oldEnabled)) {
+ mService.scheduleUpdateClientsIfNeededLocked(mService.getCurrentUserState());
+ }
+ }
+ if (stop) {
mA11yController.stopTrace();
- mService.scheduleUpdateClientsIfNeeded(mService.getCurrentUserState());
}
}
@Override
- public void logTrace(String where) {
- logTrace(where, "");
+ public void logTrace(String where, long loggingTypes) {
+ logTrace(where, loggingTypes, "");
}
@Override
- public void logTrace(String where, String callingParams) {
- mA11yController.logTrace(where, callingParams, "".getBytes(),
- Binder.getCallingUid(), Thread.currentThread().getStackTrace());
- }
-
- @Override
- public void logTrace(long timestamp, String where, String callingParams, int processId,
- long threadId, int callingUid, StackTraceElement[] callStack) {
- if (mA11yController.isAccessibilityTracingEnabled()) {
- mA11yController.logTrace(where, callingParams, "".getBytes(), callingUid, callStack,
- timestamp, processId, threadId);
+ public void logTrace(String where, long loggingTypes, String callingParams) {
+ if (isA11yTracingEnabledForTypes(loggingTypes)) {
+ mA11yController.logTrace(where, loggingTypes, callingParams, "".getBytes(),
+ Binder.getCallingUid(), Thread.currentThread().getStackTrace(),
+ new HashSet<String>(Arrays.asList("logTrace")));
}
}
- int onShellCommand(String cmd) {
+ @Override
+ public void logTrace(long timestamp, String where, long loggingTypes, String callingParams,
+ int processId, long threadId, int callingUid, StackTraceElement[] callStack,
+ Set<String> ignoreElementList) {
+ if (isA11yTracingEnabledForTypes(loggingTypes)) {
+ mA11yController.logTrace(where, loggingTypes, callingParams, "".getBytes(), callingUid,
+ callStack, timestamp, processId, threadId,
+ ((ignoreElementList == null) ? new HashSet<String>() : ignoreElementList));
+ }
+ }
+
+ private boolean needToNotifyClients(long otherTypesEnabled) {
+ return (mEnabledLoggingFlags & FLAGS_ACCESSIBILITY_MANAGER_CLIENT_STATES)
+ != (otherTypesEnabled & FLAGS_ACCESSIBILITY_MANAGER_CLIENT_STATES);
+ }
+
+ int onShellCommand(String cmd, ShellCommand shell) {
switch (cmd) {
case "start-trace": {
- startTrace();
+ String opt = shell.getNextOption();
+ if (opt == null) {
+ startTrace(FLAGS_LOGGING_ALL);
+ return 0;
+ }
+ List<String> types = new ArrayList<String>();
+ while (opt != null) {
+ switch (opt) {
+ case "-t": {
+ String type = shell.getNextArg();
+ while (type != null) {
+ types.add(type);
+ type = shell.getNextArg();
+ }
+ break;
+ }
+ default: {
+ shell.getErrPrintWriter().println(
+ "Error: option not recognized " + opt);
+ stopTrace();
+ return -1;
+ }
+ }
+ opt = shell.getNextOption();
+ }
+ long enabledTypes = AccessibilityTrace.getLoggingFlagsFromNames(types);
+ startTrace(enabledTypes);
return 0;
}
case "stop-trace": {
@@ -92,8 +217,29 @@
}
void onHelp(PrintWriter pw) {
- pw.println(" start-trace");
- pw.println(" Start the debug tracing.");
+ pw.println(" start-trace [-t LOGGING_TYPE [LOGGING_TYPE...]]");
+ pw.println(" Start the debug tracing. If no option is present, full trace will be");
+ pw.println(" generated. Options are:");
+ pw.println(" -t: Only generate tracing for the logging type(s) specified here.");
+ pw.println(" LOGGING_TYPE can be any one of below:");
+ pw.println(" IAccessibilityServiceConnection");
+ pw.println(" IAccessibilityServiceClient");
+ pw.println(" IAccessibilityManager");
+ pw.println(" IAccessibilityManagerClient");
+ pw.println(" IAccessibilityInteractionConnection");
+ pw.println(" IAccessibilityInteractionConnectionCallback");
+ pw.println(" IRemoteMagnificationAnimationCallback");
+ pw.println(" IWindowMagnificationConnection");
+ pw.println(" IWindowMagnificationConnectionCallback");
+ pw.println(" WindowManagerInternal");
+ pw.println(" WindowsForAccessibilityCallback");
+ pw.println(" MagnificationCallbacks");
+ pw.println(" InputFilter");
+ pw.println(" Gesture");
+ pw.println(" AccessibilityService");
+ pw.println(" PMBroadcastReceiver");
+ pw.println(" UserBroadcastReceiver");
+ pw.println(" FingerprintGesture");
pw.println(" stop-trace");
pw.println(" Stop the debug tracing.");
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index 0fde0de..c70bf73 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -392,7 +392,7 @@
return mBoundServices;
}
- int getClientStateLocked(boolean isUiAutomationRunning, boolean isTracingEnabled) {
+ int getClientStateLocked(boolean isUiAutomationRunning, int traceClientState) {
int clientState = 0;
final boolean a11yEnabled = isUiAutomationRunning
|| isHandlingAccessibilityEventsLocked();
@@ -408,9 +408,9 @@
if (mIsTextHighContrastEnabled) {
clientState |= AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED;
}
- if (isTracingEnabled) {
- clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_TRACING_ENABLED;
- }
+
+ clientState |= traceClientState;
+
return clientState;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index ff79469..b05dffe 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -16,6 +16,8 @@
package com.android.server.accessibility;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED;
@@ -69,6 +71,7 @@
private final AccessibilityEventSender mAccessibilityEventSender;
private final AccessibilitySecurityPolicy mSecurityPolicy;
private final AccessibilityUserManager mAccessibilityUserManager;
+ private final AccessibilityTraceManager mTraceManager;
// Connections and window tokens for cross-user windows
private final SparseArray<RemoteAccessibilityConnection>
@@ -151,6 +154,10 @@
// In some cases, onWindowsForAccessibilityChanged will be called immediately in
// setWindowsForAccessibilityCallback. We'll lost windows if flag is false.
mTrackingWindows = true;
+ if (traceWMEnabled()) {
+ logTraceWM("setWindowsForAccessibilityCallback",
+ "displayId=" + mDisplayId + ";callback=" + this);
+ }
result = mWindowManagerInternal.setWindowsForAccessibilityCallback(
mDisplayId, this);
if (!result) {
@@ -167,6 +174,10 @@
*/
void stopTrackingWindowsLocked() {
if (mTrackingWindows) {
+ if (traceWMEnabled()) {
+ logTraceWM("setWindowsForAccessibilityCallback",
+ "displayId=" + mDisplayId + ";callback=null");
+ }
mWindowManagerInternal.setWindowsForAccessibilityCallback(
mDisplayId, null);
mTrackingWindows = false;
@@ -373,6 +384,20 @@
}
}
+ /**
+ * Called when the display is reparented and becomes an embedded
+ * display.
+ *
+ * @param embeddedDisplayId The embedded display Id.
+ */
+ @Override
+ public void onDisplayReparented(int embeddedDisplayId) {
+ // Removes the un-used window observer for the embedded display.
+ synchronized (mLock) {
+ mDisplayWindowsObservers.remove(embeddedDisplayId);
+ }
+ }
+
private boolean shouldUpdateWindowsLocked(boolean forceSend,
@NonNull List<WindowInfo> windows) {
if (forceSend) {
@@ -844,19 +869,21 @@
}
/**
- * Constructor for AccessibilityManagerService.
+ * Constructor for AccessibilityWindowManager.
*/
public AccessibilityWindowManager(@NonNull Object lock, @NonNull Handler handler,
@NonNull WindowManagerInternal windowManagerInternal,
@NonNull AccessibilityEventSender accessibilityEventSender,
@NonNull AccessibilitySecurityPolicy securityPolicy,
- @NonNull AccessibilityUserManager accessibilityUserManager) {
+ @NonNull AccessibilityUserManager accessibilityUserManager,
+ @NonNull AccessibilityTraceManager traceManager) {
mLock = lock;
mHandler = handler;
mWindowManagerInternal = windowManagerInternal;
mAccessibilityEventSender = accessibilityEventSender;
mSecurityPolicy = securityPolicy;
mAccessibilityUserManager = accessibilityUserManager;
+ mTraceManager = traceManager;
}
/**
@@ -957,6 +984,9 @@
final int windowId;
boolean shouldComputeWindows = false;
final IBinder token = window.asBinder();
+ if (traceWMEnabled()) {
+ logTraceWM("getDisplayIdForWindow", "token=" + token);
+ }
final int displayId = mWindowManagerInternal.getDisplayIdForWindow(token);
synchronized (mLock) {
// We treat calls from a profile as if made by its parent as profiles
@@ -1003,9 +1033,15 @@
registerIdLocked(leashToken, windowId);
}
if (shouldComputeWindows) {
+ if (traceWMEnabled()) {
+ logTraceWM("computeWindowsForAccessibility", "displayId=" + displayId);
+ }
mWindowManagerInternal.computeWindowsForAccessibility(displayId);
}
-
+ if (traceWMEnabled()) {
+ logTraceWM("setAccessibilityIdToSurfaceMetadata",
+ "token=" + token + ";windowId=" + windowId);
+ }
mWindowManagerInternal.setAccessibilityIdToSurfaceMetadata(token, windowId);
return windowId;
}
@@ -1139,6 +1175,10 @@
mActiveWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
}
if (binder != null) {
+ if (traceWMEnabled()) {
+ logTraceWM("setAccessibilityIdToSurfaceMetadata", "token=" + binder
+ + ";windowId=AccessibilityWindowInfo.UNDEFINED_WINDOW_ID");
+ }
mWindowManagerInternal.setAccessibilityIdToSurfaceMetadata(
binder, AccessibilityWindowInfo.UNDEFINED_WINDOW_ID);
}
@@ -1169,6 +1209,9 @@
* @return The userId
*/
public int getWindowOwnerUserId(@NonNull IBinder windowToken) {
+ if (traceWMEnabled()) {
+ logTraceWM("getWindowOwnerUserId", "token=" + windowToken);
+ }
return mWindowManagerInternal.getWindowOwnerUserId(windowToken);
}
@@ -1547,6 +1590,10 @@
for (int i = 0; i < connectionList.size(); i++) {
final RemoteAccessibilityConnection connection = connectionList.get(i);
if (connection != null) {
+ if (traceIntConnEnabled()) {
+ logTraceIntConn("notifyOutsideTouch");
+ }
+
try {
connection.getRemote().notifyOutsideTouch();
} catch (RemoteException re) {
@@ -1567,6 +1614,9 @@
*/
public int getDisplayIdByUserIdAndWindowIdLocked(int userId, int windowId) {
final IBinder windowToken = getWindowTokenForUserAndWindowIdLocked(userId, windowId);
+ if (traceWMEnabled()) {
+ logTraceWM("getDisplayIdForWindow", "token=" + windowToken);
+ }
final int displayId = mWindowManagerInternal.getDisplayIdForWindow(windowToken);
return displayId;
}
@@ -1595,6 +1645,9 @@
* @return The input focused windowId, or -1 if not found
*/
private int findFocusedWindowId(int userId) {
+ if (traceWMEnabled()) {
+ logTraceWM("getFocusedWindowToken", "");
+ }
final IBinder token = mWindowManagerInternal.getFocusedWindowToken();
synchronized (mLock) {
return findWindowIdLocked(userId, token);
@@ -1644,6 +1697,9 @@
return;
}
}
+ if (traceIntConnEnabled()) {
+ logTraceIntConn("notifyOutsideTouch");
+ }
try {
connection.getRemote().clearAccessibilityFocus();
} catch (RemoteException re) {
@@ -1666,6 +1722,25 @@
return null;
}
+ private boolean traceWMEnabled() {
+ return mTraceManager.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL);
+ }
+
+ private void logTraceWM(String methodName, String params) {
+ mTraceManager.logTrace("WindowManagerInternal." + methodName,
+ FLAGS_WINDOW_MANAGER_INTERNAL, params);
+ }
+
+ private boolean traceIntConnEnabled() {
+ return mTraceManager.isA11yTracingEnabledForTypes(
+ FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION);
+ }
+
+ private void logTraceIntConn(String methodName) {
+ mTraceManager.logTrace(
+ LOG_TAG + "." + methodName, FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION);
+ }
+
/**
* Associate the token of the embedded view hierarchy to the host view hierarchy.
*
diff --git a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
index f5b0eb1..95f3560 100644
--- a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
+++ b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
@@ -16,6 +16,7 @@
package com.android.server.accessibility;
+import android.accessibilityservice.AccessibilityTrace;
import android.annotation.NonNull;
import android.content.ContentResolver;
import android.content.Context;
@@ -56,6 +57,7 @@
private static final String LOG_TAG = AutoclickController.class.getSimpleName();
+ private final AccessibilityTraceManager mTrace;
private final Context mContext;
private final int mUserId;
@@ -63,13 +65,18 @@
private ClickScheduler mClickScheduler;
private ClickDelayObserver mClickDelayObserver;
- public AutoclickController(Context context, int userId) {
+ public AutoclickController(Context context, int userId, AccessibilityTraceManager trace) {
+ mTrace = trace;
mContext = context;
mUserId = userId;
}
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (mTrace.isA11yTracingEnabledForTypes(AccessibilityTrace.FLAGS_INPUT_FILTER)) {
+ mTrace.logTrace(LOG_TAG + ".onMotionEvent", AccessibilityTrace.FLAGS_INPUT_FILTER,
+ "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
+ }
if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
if (mClickScheduler == null) {
Handler handler = new Handler(mContext.getMainLooper());
@@ -89,6 +96,10 @@
@Override
public void onKeyEvent(KeyEvent event, int policyFlags) {
+ if (mTrace.isA11yTracingEnabledForTypes(AccessibilityTrace.FLAGS_INPUT_FILTER)) {
+ mTrace.logTrace(LOG_TAG + ".onKeyEvent", AccessibilityTrace.FLAGS_INPUT_FILTER,
+ "event=" + event + ";policyFlags=" + policyFlags);
+ }
if (mClickScheduler != null) {
if (KeyEvent.isModifierKey(event.getKeyCode())) {
mClickScheduler.updateMetaState(event.getMetaState());
diff --git a/services/accessibility/java/com/android/server/accessibility/KeyboardInterceptor.java b/services/accessibility/java/com/android/server/accessibility/KeyboardInterceptor.java
index bc379c2..b8250c0 100644
--- a/services/accessibility/java/com/android/server/accessibility/KeyboardInterceptor.java
+++ b/services/accessibility/java/com/android/server/accessibility/KeyboardInterceptor.java
@@ -16,6 +16,8 @@
package com.android.server.accessibility;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_INPUT_FILTER;
+
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
@@ -64,6 +66,10 @@
@Override
public void onKeyEvent(KeyEvent event, int policyFlags) {
+ if (mAms.getTraceManager().isA11yTracingEnabledForTypes(FLAGS_INPUT_FILTER)) {
+ mAms.getTraceManager().logTrace(LOG_TAG + ".onKeyEvent",
+ FLAGS_INPUT_FILTER, "event=" + event + ";policyFlags=" + policyFlags);
+ }
/*
* Certain keys have system-level behavior that affects accessibility services.
* Let that behavior settle before handling the keys
diff --git a/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java b/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java
index 3310cb4..0b74653 100644
--- a/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java
+++ b/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java
@@ -16,6 +16,7 @@
package com.android.server.accessibility;
+import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.GestureDescription;
import android.accessibilityservice.GestureDescription.GestureStep;
import android.accessibilityservice.GestureDescription.TouchPoint;
@@ -68,6 +69,7 @@
private final Handler mHandler;
private final SparseArray<Boolean> mOpenGesturesInProgress = new SparseArray<>();
+ private final AccessibilityTraceManager mTrace;
private IAccessibilityServiceClient mServiceInterfaceForCurrentGesture;
private IntArray mSequencesInProgress = new IntArray(5);
private boolean mIsDestroyed = false;
@@ -80,15 +82,17 @@
/**
* @param looper A looper on the main thread to use for dispatching new events
*/
- public MotionEventInjector(Looper looper) {
+ public MotionEventInjector(Looper looper, AccessibilityTraceManager trace) {
mHandler = new Handler(looper, this);
+ mTrace = trace;
}
/**
* @param handler A handler to post messages. Exposes internal state for testing only.
*/
- public MotionEventInjector(Handler handler) {
+ public MotionEventInjector(Handler handler, AccessibilityTraceManager trace) {
mHandler = handler;
+ mTrace = trace;
}
/**
@@ -112,6 +116,12 @@
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (mTrace.isA11yTracingEnabledForTypes(
+ AccessibilityTrace.FLAGS_INPUT_FILTER | AccessibilityTrace.FLAGS_GESTURE)) {
+ mTrace.logTrace(LOG_TAG + ".onMotionEvent",
+ AccessibilityTrace.FLAGS_INPUT_FILTER | AccessibilityTrace.FLAGS_GESTURE,
+ "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
+ }
// MotionEventInjector would cancel any injected gesture when any MotionEvent arrives.
// For user using an external device to control the pointer movement, it's almost
// impossible to perform the gestures. Any slightly unintended movement results in the
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index 9547280..6cd23fc 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -17,6 +17,7 @@
package com.android.server.accessibility;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.annotation.Nullable;
import android.app.UiAutomation;
@@ -269,6 +270,14 @@
// If the serviceInterface is null, the UiAutomation has been shut down on
// another thread.
if (serviceInterface != null) {
+ if (mTrace.isA11yTracingEnabledForTypes(
+ AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) {
+ mTrace.logTrace("UiAutomationService.connectServiceUnknownThread",
+ AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT,
+ "serviceConnection=" + this + ";connectionId=" + mId
+ + "windowToken="
+ + mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
+ }
serviceInterface.init(this, mId,
mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index e1af2c4..f95de4e 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -16,6 +16,8 @@
package com.android.server.accessibility.gestures;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_GESTURE;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_INPUT_FILTER;
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_HOVER_ENTER;
@@ -82,6 +84,7 @@
implements GestureManifold.Listener {
static final boolean DEBUG = false;
+ private static final long LOGGING_FLAGS = FLAGS_GESTURE | FLAGS_INPUT_FILTER;
// Tag for logging received events.
private static final String LOG_TAG = "TouchExplorer";
@@ -254,6 +257,10 @@
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
+ mAms.getTraceManager().logTrace(LOG_TAG + ".onMotionEvent", LOGGING_FLAGS,
+ "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
+ }
if (!event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) {
super.onMotionEvent(event, rawEvent, policyFlags);
return;
@@ -303,6 +310,10 @@
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
+ if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
+ mAms.getTraceManager().logTrace(LOG_TAG + ".onAccessibilityEvent",
+ LOGGING_FLAGS, "event=" + event);
+ }
final int eventType = event.getEventType();
if (eventType == TYPE_VIEW_HOVER_EXIT) {
@@ -341,6 +352,10 @@
@Override
public void onDoubleTapAndHold(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
+ mAms.getTraceManager().logTrace(LOG_TAG + ".onDoubleTapAndHold", LOGGING_FLAGS,
+ "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
+ }
if (mDispatcher.longPressWithTouchEvents(event, policyFlags)) {
sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
if (isSendMotionEventsEnabled()) {
@@ -357,6 +372,10 @@
@Override
public boolean onDoubleTap(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
+ mAms.getTraceManager().logTrace(LOG_TAG + ".onDoubleTap", LOGGING_FLAGS,
+ "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
+ }
mAms.onTouchInteractionEnd();
// Remove pending event deliveries.
mSendHoverEnterAndMoveDelayed.cancel();
@@ -389,6 +408,9 @@
@Override
public boolean onGestureStarted() {
+ if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
+ mAms.getTraceManager().logTrace(LOG_TAG + ".onGestureStarted", LOGGING_FLAGS);
+ }
// We have to perform gesture detection, so
// clear the current state and try to detect.
mSendHoverEnterAndMoveDelayed.cancel();
@@ -402,6 +424,10 @@
@Override
public boolean onGestureCompleted(AccessibilityGestureEvent gestureEvent) {
+ if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
+ mAms.getTraceManager().logTrace(LOG_TAG + ".onGestureCompleted",
+ LOGGING_FLAGS, "event=" + gestureEvent);
+ }
endGestureDetection(true);
mSendTouchInteractionEndDelayed.cancel();
dispatchGesture(gestureEvent);
@@ -410,6 +436,10 @@
@Override
public boolean onGestureCancelled(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
+ mAms.getTraceManager().logTrace(LOG_TAG + ".onGestureCancelled", LOGGING_FLAGS,
+ "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
+ }
if (mState.isGestureDetecting()) {
endGestureDetection(event.getActionMasked() == ACTION_UP);
return true;
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index 1f66bfd..218c851 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -16,6 +16,8 @@
package com.android.server.accessibility.magnification;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
+
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
@@ -46,6 +48,7 @@
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.accessibility.AccessibilityManagerService;
+import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.wm.WindowManagerInternal;
import java.util.Locale;
@@ -135,6 +138,10 @@
*/
@GuardedBy("mLock")
boolean register() {
+ if (traceEnabled()) {
+ logTrace("setMagnificationCallbacks",
+ "displayID=" + mDisplayId + ";callback=" + this);
+ }
mRegistered = mControllerCtx.getWindowManager().setMagnificationCallbacks(
mDisplayId, this);
if (!mRegistered) {
@@ -142,6 +149,10 @@
return false;
}
mSpecAnimationBridge.setEnabled(true);
+ if (traceEnabled()) {
+ logTrace("getMagnificationRegion",
+ "displayID=" + mDisplayId + ";region=" + mMagnificationRegion);
+ }
// Obtain initial state.
mControllerCtx.getWindowManager().getMagnificationRegion(
mDisplayId, mMagnificationRegion);
@@ -162,6 +173,10 @@
void unregister(boolean delete) {
if (mRegistered) {
mSpecAnimationBridge.setEnabled(false);
+ if (traceEnabled()) {
+ logTrace("setMagnificationCallbacks",
+ "displayID=" + mDisplayId + ";callback=null");
+ }
mControllerCtx.getWindowManager().setMagnificationCallbacks(
mDisplayId, null);
mMagnificationRegion.setEmpty();
@@ -431,6 +446,10 @@
void setForceShowMagnifiableBounds(boolean show) {
if (mRegistered) {
mForceShowMagnifiableBounds = show;
+ if (traceEnabled()) {
+ logTrace("setForceShowMagnifiableBounds",
+ "displayID=" + mDisplayId + ";show=" + show);
+ }
mControllerCtx.getWindowManager().setForceShowMagnifiableBounds(
mDisplayId, show);
}
@@ -1255,6 +1274,16 @@
}
}
+ private boolean traceEnabled() {
+ return mControllerCtx.getTraceManager().isA11yTracingEnabledForTypes(
+ FLAGS_WINDOW_MANAGER_INTERNAL);
+ }
+
+ private void logTrace(String methodName, String params) {
+ mControllerCtx.getTraceManager().logTrace(
+ "WindowManagerInternal." + methodName, FLAGS_WINDOW_MANAGER_INTERNAL, params);
+ }
+
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
@@ -1320,6 +1349,13 @@
mEnabled = enabled;
if (!mEnabled) {
mSentMagnificationSpec.clear();
+ if (mControllerCtx.getTraceManager().isA11yTracingEnabledForTypes(
+ FLAGS_WINDOW_MANAGER_INTERNAL)) {
+ mControllerCtx.getTraceManager().logTrace(
+ "WindowManagerInternal.setMagnificationSpec",
+ FLAGS_WINDOW_MANAGER_INTERNAL,
+ "displayID=" + mDisplayId + ";spec=" + mSentMagnificationSpec);
+ }
mControllerCtx.getWindowManager().setMagnificationSpec(
mDisplayId, mSentMagnificationSpec);
}
@@ -1367,6 +1403,13 @@
}
mSentMagnificationSpec.setTo(spec);
+ if (mControllerCtx.getTraceManager().isA11yTracingEnabledForTypes(
+ FLAGS_WINDOW_MANAGER_INTERNAL)) {
+ mControllerCtx.getTraceManager().logTrace(
+ "WindowManagerInternal.setMagnificationSpec",
+ FLAGS_WINDOW_MANAGER_INTERNAL,
+ "displayID=" + mDisplayId + ";spec=" + mSentMagnificationSpec);
+ }
mControllerCtx.getWindowManager().setMagnificationSpec(
mDisplayId, mSentMagnificationSpec);
}
@@ -1455,6 +1498,7 @@
public static class ControllerContext {
private final Context mContext;
private final AccessibilityManagerService mAms;
+ private final AccessibilityTraceManager mTrace;
private final WindowManagerInternal mWindowManager;
private final Handler mHandler;
private final Long mAnimationDuration;
@@ -1469,6 +1513,7 @@
long animationDuration) {
mContext = context;
mAms = ams;
+ mTrace = ams.getTraceManager();
mWindowManager = windowManager;
mHandler = handler;
mAnimationDuration = animationDuration;
@@ -1491,6 +1536,14 @@
}
/**
+ * @return AccessibilityTraceManager
+ */
+ @NonNull
+ public AccessibilityTraceManager getTraceManager() {
+ return mTrace;
+ }
+
+ /**
* @return WindowManagerInternal
*/
@NonNull
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index f7d1b9a..c3d8d4c 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -61,6 +61,7 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.accessibility.AccessibilityManagerService;
+import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.accessibility.gestures.GestureUtils;
/**
@@ -142,12 +143,13 @@
public FullScreenMagnificationGestureHandler(@UiContext Context context,
FullScreenMagnificationController fullScreenMagnificationController,
+ AccessibilityTraceManager trace,
Callback callback,
boolean detectTripleTap,
boolean detectShortcutTrigger,
@NonNull WindowMagnificationPromptController promptController,
int displayId) {
- super(displayId, detectTripleTap, detectShortcutTrigger, callback);
+ super(displayId, detectTripleTap, detectShortcutTrigger, trace, callback);
if (DEBUG_ALL) {
Log.i(mLogTag,
"FullScreenMagnificationGestureHandler(detectTripleTap = " + detectTripleTap
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index f9aecd7..8aacafb 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -411,8 +411,7 @@
synchronized (mLock) {
if (mWindowMagnificationMgr == null) {
mWindowMagnificationMgr = new WindowMagnificationManager(mContext,
- mAms.getCurrentUserIdLocked(),
- this);
+ mAms.getCurrentUserIdLocked(), this, mAms.getTraceManager());
}
return mWindowMagnificationMgr;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
index bbe40b6..19b3396 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
@@ -20,11 +20,13 @@
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_UP;
+import android.accessibilityservice.AccessibilityTrace;
import android.annotation.NonNull;
import android.util.Log;
import android.util.Slog;
import android.view.MotionEvent;
+import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.accessibility.BaseEventStreamTransformation;
import java.util.ArrayDeque;
@@ -99,14 +101,17 @@
void onTripleTapped(int displayId, int mode);
}
+ private final AccessibilityTraceManager mTrace;
protected final Callback mCallback;
protected MagnificationGestureHandler(int displayId, boolean detectTripleTap,
boolean detectShortcutTrigger,
+ AccessibilityTraceManager trace,
@NonNull Callback callback) {
mDisplayId = displayId;
mDetectTripleTap = detectTripleTap;
mDetectShortcutTrigger = detectShortcutTrigger;
+ mTrace = trace;
mCallback = callback;
mDebugInputEventHistory = DEBUG_EVENT_STREAM ? new ArrayDeque<>() : null;
@@ -118,6 +123,12 @@
if (DEBUG_ALL) {
Slog.i(mLogTag, "onMotionEvent(" + event + ")");
}
+ if (mTrace.isA11yTracingEnabledForTypes(
+ AccessibilityTrace.FLAGS_INPUT_FILTER | AccessibilityTrace.FLAGS_GESTURE)) {
+ mTrace.logTrace("MagnificationGestureHandler.onMotionEvent",
+ AccessibilityTrace.FLAGS_INPUT_FILTER | AccessibilityTrace.FLAGS_GESTURE,
+ "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
+ }
if (DEBUG_EVENT_STREAM) {
storeEventInto(mDebugInputEventHistory, event);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java
index 993027d..5277425 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java
@@ -16,6 +16,9 @@
package com.android.server.accessibility.magnification;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK;
import static android.os.IBinder.DeathRecipient;
import android.annotation.NonNull;
@@ -27,6 +30,8 @@
import android.view.accessibility.IWindowMagnificationConnectionCallback;
import android.view.accessibility.MagnificationAnimationCallback;
+import com.android.server.accessibility.AccessibilityTraceManager;
+
/**
* A wrapper of {@link IWindowMagnificationConnection}.
*/
@@ -36,9 +41,12 @@
private static final String TAG = "WindowMagnificationConnectionWrapper";
private final @NonNull IWindowMagnificationConnection mConnection;
+ private final @NonNull AccessibilityTraceManager mTrace;
- WindowMagnificationConnectionWrapper(@NonNull IWindowMagnificationConnection connection) {
+ WindowMagnificationConnectionWrapper(@NonNull IWindowMagnificationConnection connection,
+ @NonNull AccessibilityTraceManager trace) {
mConnection = connection;
+ mTrace = trace;
}
//Should not use this instance anymore after calling it.
@@ -52,9 +60,15 @@
boolean enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
@Nullable MagnificationAnimationCallback callback) {
+ if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+ mTrace.logTrace(TAG + ".enableWindowMagnification",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
+ "displayId=" + displayId + ";scale=" + scale + ";centerX=" + centerX
+ + ";centerY=" + centerY + ";callback=" + callback);
+ }
try {
mConnection.enableWindowMagnification(displayId, scale, centerX, centerY,
- transformToRemoteCallback(callback));
+ transformToRemoteCallback(callback, mTrace));
} catch (RemoteException e) {
if (DBG) {
Slog.e(TAG, "Error calling enableWindowMagnification()", e);
@@ -65,6 +79,10 @@
}
boolean setScale(int displayId, float scale) {
+ if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+ mTrace.logTrace(TAG + ".setScale", FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
+ "displayId=" + displayId + ";scale=" + scale);
+ }
try {
mConnection.setScale(displayId, scale);
} catch (RemoteException e) {
@@ -78,8 +96,14 @@
boolean disableWindowMagnification(int displayId,
@Nullable MagnificationAnimationCallback callback) {
+ if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+ mTrace.logTrace(TAG + ".disableWindowMagnification",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
+ "displayId=" + displayId + ";callback=" + callback);
+ }
try {
- mConnection.disableWindowMagnification(displayId, transformToRemoteCallback(callback));
+ mConnection.disableWindowMagnification(displayId,
+ transformToRemoteCallback(callback, mTrace));
} catch (RemoteException e) {
if (DBG) {
Slog.e(TAG, "Error calling disableWindowMagnification()", e);
@@ -90,6 +114,10 @@
}
boolean moveWindowMagnifier(int displayId, float offsetX, float offsetY) {
+ if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+ mTrace.logTrace(TAG + ".moveWindowMagnifier", FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
+ "displayId=" + displayId + ";offsetX=" + offsetX + ";offsetY=" + offsetY);
+ }
try {
mConnection.moveWindowMagnifier(displayId, offsetX, offsetY);
} catch (RemoteException e) {
@@ -102,6 +130,11 @@
}
boolean showMagnificationButton(int displayId, int magnificationMode) {
+ if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+ mTrace.logTrace(TAG + ".showMagnificationButton",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
+ "displayId=" + displayId + ";mode=" + magnificationMode);
+ }
try {
mConnection.showMagnificationButton(displayId, magnificationMode);
} catch (RemoteException e) {
@@ -114,6 +147,10 @@
}
boolean removeMagnificationButton(int displayId) {
+ if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+ mTrace.logTrace(TAG + ".removeMagnificationButton",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION, "displayId=" + displayId);
+ }
try {
mConnection.removeMagnificationButton(displayId);
} catch (RemoteException e) {
@@ -126,6 +163,14 @@
}
boolean setConnectionCallback(IWindowMagnificationConnectionCallback connectionCallback) {
+ if (mTrace.isA11yTracingEnabledForTypes(
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION
+ | FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) {
+ mTrace.logTrace(TAG + ".setConnectionCallback",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION
+ | FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
+ "callback=" + connectionCallback);
+ }
try {
mConnection.setConnectionCallback(connectionCallback);
} catch (RemoteException e) {
@@ -139,25 +184,38 @@
private static @Nullable
IRemoteMagnificationAnimationCallback transformToRemoteCallback(
- MagnificationAnimationCallback callback) {
+ MagnificationAnimationCallback callback, AccessibilityTraceManager trace) {
if (callback == null) {
return null;
}
- return new RemoteAnimationCallback(callback);
+ return new RemoteAnimationCallback(callback, trace);
}
private static class RemoteAnimationCallback extends
IRemoteMagnificationAnimationCallback.Stub {
-
private final MagnificationAnimationCallback mCallback;
+ private final AccessibilityTraceManager mTrace;
- RemoteAnimationCallback(@NonNull MagnificationAnimationCallback callback) {
+ RemoteAnimationCallback(@NonNull MagnificationAnimationCallback callback,
+ @NonNull AccessibilityTraceManager trace) {
mCallback = callback;
+ mTrace = trace;
+ if (mTrace.isA11yTracingEnabledForTypes(
+ FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK)) {
+ mTrace.logTrace("RemoteAnimationCallback.constructor",
+ FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK, "callback=" + callback);
+ }
}
@Override
public void onResult(boolean success) throws RemoteException {
mCallback.onResult(success);
+ if (mTrace.isA11yTracingEnabledForTypes(
+ FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK)) {
+ mTrace.logTrace("RemoteAnimationCallback.onResult",
+ FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK, "success=" + success);
+ }
+
}
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
index 4fb9a03..b26d364 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
@@ -34,6 +34,7 @@
import android.view.MotionEvent;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.accessibility.EventStreamTransformation;
import com.android.server.accessibility.gestures.MultiTap;
import com.android.server.accessibility.gestures.MultiTapAndHold;
@@ -89,9 +90,10 @@
public WindowMagnificationGestureHandler(@UiContext Context context,
WindowMagnificationManager windowMagnificationMgr,
+ AccessibilityTraceManager trace,
Callback callback,
boolean detectTripleTap, boolean detectShortcutTrigger, int displayId) {
- super(displayId, detectTripleTap, detectShortcutTrigger, callback);
+ super(displayId, detectTripleTap, detectShortcutTrigger, trace, callback);
if (DEBUG_ALL) {
Slog.i(mLogTag,
"WindowMagnificationGestureHandler() , displayId = " + displayId + ")");
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
index 938cb73..7a111d8 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -16,6 +16,9 @@
package com.android.server.accessibility.magnification;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.BroadcastReceiver;
@@ -39,6 +42,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
+import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.statusbar.StatusBarManagerInternal;
/**
@@ -111,11 +115,14 @@
}
private final Callback mCallback;
+ private final AccessibilityTraceManager mTrace;
- public WindowMagnificationManager(Context context, int userId, @NonNull Callback callback) {
+ public WindowMagnificationManager(Context context, int userId, @NonNull Callback callback,
+ AccessibilityTraceManager trace) {
mContext = context;
mUserId = userId;
mCallback = callback;
+ mTrace = trace;
}
/**
@@ -135,7 +142,7 @@
mConnectionWrapper = null;
}
if (connection != null) {
- mConnectionWrapper = new WindowMagnificationConnectionWrapper(connection);
+ mConnectionWrapper = new WindowMagnificationConnectionWrapper(connection, mTrace);
}
if (mConnectionWrapper != null) {
@@ -197,7 +204,10 @@
}
}
}
-
+ if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+ mTrace.logTrace(TAG + ".requestWindowMagnificationConnection",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION, "connect=" + connect);
+ }
final long identity = Binder.clearCallingIdentity();
try {
final StatusBarManagerInternal service = LocalServices.getService(
@@ -511,6 +521,12 @@
@Override
public void onWindowMagnifierBoundsChanged(int displayId, Rect bounds) {
+ if (mTrace.isA11yTracingEnabledForTypes(
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) {
+ mTrace.logTrace(TAG + "ConnectionCallback.onWindowMagnifierBoundsChanged",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
+ "displayId=" + displayId + ";bounds=" + bounds);
+ }
synchronized (mLock) {
WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
if (magnifier == null) {
@@ -527,11 +543,23 @@
@Override
public void onChangeMagnificationMode(int displayId, int magnificationMode)
throws RemoteException {
+ if (mTrace.isA11yTracingEnabledForTypes(
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) {
+ mTrace.logTrace(TAG + "ConnectionCallback.onChangeMagnificationMode",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
+ "displayId=" + displayId + ";mode=" + magnificationMode);
+ }
//TODO: Uses this method to change the magnification mode on non-default display.
}
@Override
public void onSourceBoundsChanged(int displayId, Rect sourceBounds) {
+ if (mTrace.isA11yTracingEnabledForTypes(
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) {
+ mTrace.logTrace(TAG + "ConnectionCallback.onSourceBoundsChanged",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
+ "displayId=" + displayId + ";source=" + sourceBounds);
+ }
synchronized (mLock) {
WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
if (magnifier == null) {
@@ -543,11 +571,23 @@
@Override
public void onPerformScaleAction(int displayId, float scale) {
+ if (mTrace.isA11yTracingEnabledForTypes(
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) {
+ mTrace.logTrace(TAG + "ConnectionCallback.onPerformScaleAction",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
+ "displayId=" + displayId + ";scale=" + scale);
+ }
mCallback.onPerformScaleAction(displayId, scale);
}
@Override
public void onAccessibilityActionPerformed(int displayId) {
+ if (mTrace.isA11yTracingEnabledForTypes(
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) {
+ mTrace.logTrace(TAG + "ConnectionCallback.onAccessibilityActionPerformed",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
+ "displayId=" + displayId);
+ }
mCallback.onAccessibilityActionPerformed(displayId);
}
diff --git a/services/autofill/java/com/android/server/autofill/ClientSuggestionsSession.java b/services/autofill/java/com/android/server/autofill/ClientSuggestionsSession.java
new file mode 100644
index 0000000..715697d
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/ClientSuggestionsSession.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.autofill;
+
+import static android.service.autofill.FillRequest.INVALID_REQUEST_ID;
+
+import static com.android.server.autofill.Helper.sVerbose;
+
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.AppGlobals;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.ICancellationSignal;
+import android.os.RemoteException;
+import android.service.autofill.Dataset;
+import android.service.autofill.FillResponse;
+import android.service.autofill.IFillCallback;
+import android.service.autofill.SaveInfo;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.util.Slog;
+import android.view.autofill.AutofillId;
+import android.view.autofill.IAutoFillManagerClient;
+import android.view.inputmethod.InlineSuggestionsRequest;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.infra.AndroidFuture;
+
+import java.util.List;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Maintains a client suggestions session with the
+ * {@link android.view.autofill.AutofillRequestCallback} through the {@link IAutoFillManagerClient}.
+ *
+ */
+final class ClientSuggestionsSession {
+
+ private static final String TAG = "ClientSuggestionsSession";
+ private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 15 * DateUtils.SECOND_IN_MILLIS;
+
+ private final int mSessionId;
+ private final IAutoFillManagerClient mClient;
+ private final Handler mHandler;
+ private final ComponentName mComponentName;
+
+ private final RemoteFillService.FillServiceCallbacks mCallbacks;
+
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private AndroidFuture<FillResponse> mPendingFillRequest;
+ @GuardedBy("mLock")
+ private int mPendingFillRequestId = INVALID_REQUEST_ID;
+
+ ClientSuggestionsSession(int sessionId, IAutoFillManagerClient client, Handler handler,
+ ComponentName componentName, RemoteFillService.FillServiceCallbacks callbacks) {
+ mSessionId = sessionId;
+ mClient = client;
+ mHandler = handler;
+ mComponentName = componentName;
+ mCallbacks = callbacks;
+ }
+
+ void onFillRequest(int requestId, InlineSuggestionsRequest inlineRequest, int flags) {
+ final AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>();
+ final AtomicReference<AndroidFuture<FillResponse>> futureRef = new AtomicReference<>();
+ final AndroidFuture<FillResponse> fillRequest = new AndroidFuture<>();
+
+ mHandler.post(() -> {
+ if (sVerbose) {
+ Slog.v(TAG, "calling onFillRequest() for id=" + requestId);
+ }
+
+ try {
+ mClient.requestFillFromClient(requestId, inlineRequest,
+ new FillCallbackImpl(fillRequest, futureRef, cancellationSink));
+ } catch (RemoteException e) {
+ fillRequest.completeExceptionally(e);
+ }
+ });
+
+ fillRequest.orTimeout(TIMEOUT_REMOTE_REQUEST_MILLIS, TimeUnit.MILLISECONDS);
+ futureRef.set(fillRequest);
+
+ synchronized (mLock) {
+ mPendingFillRequest = fillRequest;
+ mPendingFillRequestId = requestId;
+ }
+
+ fillRequest.whenComplete((res, err) -> mHandler.post(() -> {
+ synchronized (mLock) {
+ mPendingFillRequest = null;
+ mPendingFillRequestId = INVALID_REQUEST_ID;
+ }
+ if (err == null) {
+ processAutofillId(res);
+ mCallbacks.onFillRequestSuccess(requestId, res,
+ mComponentName.getPackageName(), flags);
+ } else {
+ Slog.e(TAG, "Error calling on client fill request", err);
+ if (err instanceof TimeoutException) {
+ dispatchCancellationSignal(cancellationSink.get());
+ mCallbacks.onFillRequestTimeout(requestId);
+ } else if (err instanceof CancellationException) {
+ dispatchCancellationSignal(cancellationSink.get());
+ } else {
+ mCallbacks.onFillRequestFailure(requestId, err.getMessage());
+ }
+ }
+ }));
+ }
+
+ /**
+ * Gets the application info for the component.
+ */
+ @Nullable
+ static ApplicationInfo getAppInfo(ComponentName comp, @UserIdInt int userId) {
+ try {
+ ApplicationInfo si = AppGlobals.getPackageManager().getApplicationInfo(
+ comp.getPackageName(),
+ PackageManager.GET_META_DATA,
+ userId);
+ if (si != null) {
+ return si;
+ }
+ } catch (RemoteException e) {
+ }
+ return null;
+ }
+
+ /**
+ * Gets the user-visible name of the application.
+ */
+ @Nullable
+ @GuardedBy("mLock")
+ static CharSequence getAppLabelLocked(Context context, ApplicationInfo appInfo) {
+ return appInfo == null ? null : appInfo.loadSafeLabel(
+ context.getPackageManager(), 0 /* do not ellipsize */,
+ TextUtils.SAFE_STRING_FLAG_FIRST_LINE | TextUtils.SAFE_STRING_FLAG_TRIM);
+ }
+
+ /**
+ * Gets the user-visible icon of the application.
+ */
+ @Nullable
+ @GuardedBy("mLock")
+ static Drawable getAppIconLocked(Context context, ApplicationInfo appInfo) {
+ return appInfo == null ? null : appInfo.loadIcon(context.getPackageManager());
+ }
+
+ int cancelCurrentRequest() {
+ synchronized (mLock) {
+ return mPendingFillRequest != null && mPendingFillRequest.cancel(false)
+ ? mPendingFillRequestId
+ : INVALID_REQUEST_ID;
+ }
+ }
+
+ /**
+ * The {@link AutofillId} which the client gets from its view is not contain the session id,
+ * but Autofill framework is using the {@link AutofillId} with a session id. So before using
+ * those ids in the Autofill framework, applies the current session id.
+ *
+ * @param res which response need to apply for a session id
+ */
+ private void processAutofillId(FillResponse res) {
+ if (res == null) {
+ return;
+ }
+
+ final List<Dataset> datasets = res.getDatasets();
+ if (datasets != null && !datasets.isEmpty()) {
+ for (int i = 0; i < datasets.size(); i++) {
+ final Dataset dataset = datasets.get(i);
+ if (dataset != null) {
+ applySessionId(dataset.getFieldIds());
+ }
+ }
+ }
+
+ final SaveInfo saveInfo = res.getSaveInfo();
+ if (saveInfo != null) {
+ applySessionId(saveInfo.getOptionalIds());
+ applySessionId(saveInfo.getRequiredIds());
+ applySessionId(saveInfo.getSanitizerValues());
+ applySessionId(saveInfo.getTriggerId());
+ }
+ }
+
+ private void applySessionId(List<AutofillId> ids) {
+ if (ids == null || ids.isEmpty()) {
+ return;
+ }
+
+ for (int i = 0; i < ids.size(); i++) {
+ applySessionId(ids.get(i));
+ }
+ }
+
+ private void applySessionId(AutofillId[][] ids) {
+ if (ids == null) {
+ return;
+ }
+ for (int i = 0; i < ids.length; i++) {
+ applySessionId(ids[i]);
+ }
+ }
+
+ private void applySessionId(AutofillId[] ids) {
+ if (ids == null) {
+ return;
+ }
+ for (int i = 0; i < ids.length; i++) {
+ applySessionId(ids[i]);
+ }
+ }
+
+ private void applySessionId(AutofillId id) {
+ if (id == null) {
+ return;
+ }
+ id.setSessionId(mSessionId);
+ }
+
+ private void dispatchCancellationSignal(@Nullable ICancellationSignal signal) {
+ if (signal == null) {
+ return;
+ }
+ try {
+ signal.cancel();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error requesting a cancellation", e);
+ }
+ }
+
+ private class FillCallbackImpl extends IFillCallback.Stub {
+ final AndroidFuture<FillResponse> mFillRequest;
+ final AtomicReference<AndroidFuture<FillResponse>> mFutureRef;
+ final AtomicReference<ICancellationSignal> mCancellationSink;
+
+ FillCallbackImpl(AndroidFuture<FillResponse> fillRequest,
+ AtomicReference<AndroidFuture<FillResponse>> futureRef,
+ AtomicReference<ICancellationSignal> cancellationSink) {
+ mFillRequest = fillRequest;
+ mFutureRef = futureRef;
+ mCancellationSink = cancellationSink;
+ }
+
+ @Override
+ public void onCancellable(ICancellationSignal cancellation) {
+ AndroidFuture<FillResponse> future = mFutureRef.get();
+ if (future != null && future.isCancelled()) {
+ dispatchCancellationSignal(cancellation);
+ } else {
+ mCancellationSink.set(cancellation);
+ }
+ }
+
+ @Override
+ public void onSuccess(FillResponse response) {
+ mFillRequest.complete(response);
+ }
+
+ @Override
+ public void onFailure(int requestId, CharSequence message) {
+ String errorMessage = message == null ? "" : String.valueOf(message);
+ mFillRequest.completeExceptionally(
+ new RuntimeException(errorMessage));
+ }
+ }
+}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 320047f..042631d 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -26,6 +26,7 @@
import static android.view.autofill.AutofillManager.ACTION_VALUE_CHANGED;
import static android.view.autofill.AutofillManager.ACTION_VIEW_ENTERED;
import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED;
+import static android.view.autofill.AutofillManager.FLAG_ENABLED_CLIENT_SUGGESTIONS;
import static android.view.autofill.AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM;
import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString;
@@ -53,6 +54,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
+import android.content.pm.ApplicationInfo;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -346,6 +348,9 @@
*/
private final AssistDataReceiverImpl mAssistReceiver = new AssistDataReceiverImpl();
+ @Nullable
+ private ClientSuggestionsSession mClientSuggestionsSession;
+
void onSwitchInputMethodLocked() {
// One caveat is that for the case where the focus is on a field for which regular autofill
// returns null, and augmented autofill is triggered, and then the user switches the input
@@ -416,6 +421,10 @@
/** Whether the current {@link FillResponse} is expired. */
@GuardedBy("mLock")
private boolean mExpiredResponse;
+
+ /** Whether the client is using {@link android.view.autofill.AutofillRequestCallback}. */
+ @GuardedBy("mLock")
+ private boolean mClientSuggestionsEnabled;
}
/**
@@ -441,13 +450,19 @@
return;
}
mPendingInlineSuggestionsRequest = inlineSuggestionsRequest;
- maybeRequestFillLocked();
+ maybeRequestFillFromServiceLocked();
viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
}
} : null;
}
- void maybeRequestFillLocked() {
+ void newAutofillRequestLocked(@Nullable InlineSuggestionsRequest inlineRequest) {
+ mPendingFillRequest = null;
+ mWaitForInlineRequest = inlineRequest != null;
+ mPendingInlineSuggestionsRequest = inlineRequest;
+ }
+
+ void maybeRequestFillFromServiceLocked() {
if (mPendingFillRequest == null) {
return;
}
@@ -457,9 +472,12 @@
return;
}
- mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(),
- mPendingFillRequest.getFillContexts(), mPendingFillRequest.getClientState(),
- mPendingFillRequest.getFlags(), mPendingInlineSuggestionsRequest);
+ if (mPendingInlineSuggestionsRequest.isServiceSupported()) {
+ mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(),
+ mPendingFillRequest.getFillContexts(),
+ mPendingFillRequest.getClientState(),
+ mPendingFillRequest.getFlags(), mPendingInlineSuggestionsRequest);
+ }
}
mRemoteFillService.onFillRequest(mPendingFillRequest);
@@ -566,7 +584,7 @@
/*inlineSuggestionsRequest=*/null);
mPendingFillRequest = request;
- maybeRequestFillLocked();
+ maybeRequestFillFromServiceLocked();
}
if (mActivityToken != null) {
@@ -728,30 +746,39 @@
}
/**
- * Cancels the last request sent to the {@link #mRemoteFillService}.
+ * Cancels the last request sent to the {@link #mRemoteFillService} or the
+ * {@link #mClientSuggestionsSession}.
*/
@GuardedBy("mLock")
private void cancelCurrentRequestLocked() {
- if (mRemoteFillService == null) {
- wtf(null, "cancelCurrentRequestLocked() called without a remote service. "
- + "mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly);
+ if (mRemoteFillService == null && mClientSuggestionsSession == null) {
+ wtf(null, "cancelCurrentRequestLocked() called without a remote service or a "
+ + "client suggestions session. mForAugmentedAutofillOnly: %s",
+ mSessionFlags.mAugmentedAutofillOnly);
return;
}
- final int canceledRequest = mRemoteFillService.cancelCurrentRequest();
- // Remove the FillContext as there will never be a response for the service
- if (canceledRequest != INVALID_REQUEST_ID && mContexts != null) {
- final int numContexts = mContexts.size();
+ if (mRemoteFillService != null) {
+ final int canceledRequest = mRemoteFillService.cancelCurrentRequest();
- // It is most likely the last context, hence search backwards
- for (int i = numContexts - 1; i >= 0; i--) {
- if (mContexts.get(i).getRequestId() == canceledRequest) {
- if (sDebug) Slog.d(TAG, "cancelCurrentRequest(): id = " + canceledRequest);
- mContexts.remove(i);
- break;
+ // Remove the FillContext as there will never be a response for the service
+ if (canceledRequest != INVALID_REQUEST_ID && mContexts != null) {
+ final int numContexts = mContexts.size();
+
+ // It is most likely the last context, hence search backwards
+ for (int i = numContexts - 1; i >= 0; i--) {
+ if (mContexts.get(i).getRequestId() == canceledRequest) {
+ if (sDebug) Slog.d(TAG, "cancelCurrentRequest(): id = " + canceledRequest);
+ mContexts.remove(i);
+ break;
+ }
}
}
}
+
+ if (mClientSuggestionsSession != null) {
+ mClientSuggestionsSession.cancelCurrentRequest();
+ }
}
private boolean isViewFocusedLocked(int flags) {
@@ -816,17 +843,30 @@
// structure is taken. This causes only one fill request per burst of focus changes.
cancelCurrentRequestLocked();
- // Only ask IME to create inline suggestions request if Autofill provider supports it and
- // the render service is available except the autofill is triggered manually and the view
- // is also not focused.
+ // Only ask IME to create inline suggestions request when
+ // 1. Autofill provider supports it or client enabled client suggestions.
+ // 2. The render service is available.
+ // 3. The view is focused. (The view may not be focused if the autofill is triggered
+ // manually.)
final RemoteInlineSuggestionRenderService remoteRenderService =
mService.getRemoteInlineSuggestionRenderServiceLocked();
- if (mSessionFlags.mInlineSupportedByService
+ if ((mSessionFlags.mInlineSupportedByService || mSessionFlags.mClientSuggestionsEnabled)
&& remoteRenderService != null
&& isViewFocusedLocked(flags)) {
- Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer =
- mAssistReceiver.newAutofillRequestLocked(viewState,
- /* isInlineRequest= */ true);
+ Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer;
+ if (mSessionFlags.mClientSuggestionsEnabled) {
+ final int finalRequestId = requestId;
+ inlineSuggestionsRequestConsumer = (inlineSuggestionsRequest) -> {
+ // Using client suggestions
+ synchronized (mLock) {
+ onClientFillRequestLocked(finalRequestId, inlineSuggestionsRequest);
+ }
+ viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
+ };
+ } else {
+ inlineSuggestionsRequestConsumer = mAssistReceiver.newAutofillRequestLocked(
+ viewState, /* isInlineRequest= */ true);
+ }
if (inlineSuggestionsRequestConsumer != null) {
final AutofillId focusedId = mCurrentViewId;
final int requestIdCopy = requestId;
@@ -842,11 +882,24 @@
);
viewState.setState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
}
+ } else if (mSessionFlags.mClientSuggestionsEnabled) {
+ // Request client suggestions for the dropdown mode
+ onClientFillRequestLocked(requestId, null);
} else {
mAssistReceiver.newAutofillRequestLocked(viewState, /* isInlineRequest= */ false);
}
+ if (mSessionFlags.mClientSuggestionsEnabled) {
+ // Using client suggestions, unnecessary request AssistStructure
+ return;
+ }
+
// Now request the assist structure data.
+ requestAssistStructureLocked(requestId, flags);
+ }
+
+ @GuardedBy("mLock")
+ private void requestAssistStructureLocked(int requestId, int flags) {
try {
final Bundle receiverExtras = new Bundle();
receiverExtras.putInt(EXTRA_REQUEST_ID, requestId);
@@ -895,10 +948,13 @@
mComponentName = componentName;
mCompatMode = compatMode;
mSessionState = STATE_ACTIVE;
+
synchronized (mLock) {
mSessionFlags = new SessionFlags();
mSessionFlags.mAugmentedAutofillOnly = forAugmentedAutofillOnly;
mSessionFlags.mInlineSupportedByService = mService.isInlineSuggestionsEnabledLocked();
+ mSessionFlags.mClientSuggestionsEnabled =
+ (mFlags & FLAG_ENABLED_CLIENT_SUGGESTIONS) != 0;
setClientLocked(client);
}
@@ -1010,12 +1066,13 @@
if (requestLog != null) {
requestLog.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS, -1);
}
- processNullResponseLocked(requestId, requestFlags);
+ processNullResponseOrFallbackLocked(requestId, requestFlags);
return;
}
fieldClassificationIds = response.getFieldClassificationIds();
- if (fieldClassificationIds != null && !mService.isFieldClassificationEnabledLocked()) {
+ if (!mSessionFlags.mClientSuggestionsEnabled && fieldClassificationIds != null
+ && !mService.isFieldClassificationEnabledLocked()) {
Slog.w(TAG, "Ignoring " + response + " because field detection is disabled");
processNullResponseLocked(requestId, requestFlags);
return;
@@ -1094,6 +1151,26 @@
}
}
+ @GuardedBy("mLock")
+ private void processNullResponseOrFallbackLocked(int requestId, int flags) {
+ if (!mSessionFlags.mClientSuggestionsEnabled) {
+ processNullResponseLocked(requestId, flags);
+ return;
+ }
+
+ // fallback to the default platform password manager
+ mSessionFlags.mClientSuggestionsEnabled = false;
+
+ final InlineSuggestionsRequest inlineRequest =
+ (mLastInlineSuggestionsRequest != null
+ && mLastInlineSuggestionsRequest.first == requestId)
+ ? mLastInlineSuggestionsRequest.second : null;
+ mAssistReceiver.newAutofillRequestLocked(inlineRequest);
+ requestAssistStructureLocked(requestId,
+ flags & ~FLAG_ENABLED_CLIENT_SUGGESTIONS);
+ return;
+ }
+
// FillServiceCallbacks
@Override
public void onFillRequestFailure(int requestId, @Nullable CharSequence message) {
@@ -3032,13 +3109,22 @@
filterText = value.getTextValue().toString();
}
- final CharSequence serviceLabel;
- final Drawable serviceIcon;
+ final CharSequence targetLabel;
+ final Drawable targetIcon;
synchronized (mLock) {
- serviceLabel = mService.getServiceLabelLocked();
- serviceIcon = mService.getServiceIconLocked();
+ if (mSessionFlags.mClientSuggestionsEnabled) {
+ final ApplicationInfo appInfo = ClientSuggestionsSession.getAppInfo(mComponentName,
+ mService.getUserId());
+ targetLabel = ClientSuggestionsSession.getAppLabelLocked(
+ mService.getMaster().getContext(), appInfo);
+ targetIcon = ClientSuggestionsSession.getAppIconLocked(
+ mService.getMaster().getContext(), appInfo);
+ } else {
+ targetLabel = mService.getServiceLabelLocked();
+ targetIcon = mService.getServiceIconLocked();
+ }
}
- if (serviceLabel == null || serviceIcon == null) {
+ if (targetLabel == null || targetIcon == null) {
wtf(null, "onFillReady(): no service label or icon");
return;
}
@@ -3058,7 +3144,7 @@
getUiForShowing().showFillUi(filledId, response, filterText,
mService.getServicePackageName(), mComponentName,
- serviceLabel, serviceIcon, this, id, mCompatMode);
+ targetLabel, targetIcon, this, id, mCompatMode);
mService.logDatasetShown(id, mClientState);
@@ -3105,6 +3191,17 @@
return false;
}
+ final InlineSuggestionsRequest request = inlineSuggestionsRequest.get();
+ if (mSessionFlags.mClientSuggestionsEnabled && !request.isClientSupported()
+ || !mSessionFlags.mClientSuggestionsEnabled && !request.isServiceSupported()) {
+ if (sDebug) {
+ Slog.d(TAG, "Inline suggestions not supported for "
+ + (mSessionFlags.mClientSuggestionsEnabled ? "client" : "service")
+ + ". Falling back to dropdown.");
+ }
+ return false;
+ }
+
final RemoteInlineSuggestionRenderService remoteRenderService =
mService.getRemoteInlineSuggestionRenderServiceLocked();
if (remoteRenderService == null) {
@@ -3113,7 +3210,7 @@
}
final InlineFillUi.InlineFillUiInfo inlineFillUiInfo =
- new InlineFillUi.InlineFillUiInfo(inlineSuggestionsRequest.get(), focusedId,
+ new InlineFillUi.InlineFillUiInfo(request, focusedId,
filterText, remoteRenderService, userId, id);
InlineFillUi inlineFillUi = InlineFillUi.forAutofill(inlineFillUiInfo, response,
new InlineFillUi.InlineSuggestionUiCallback() {
@@ -3715,6 +3812,25 @@
}
}
+ @GuardedBy("mLock")
+ private void onClientFillRequestLocked(int requestId,
+ InlineSuggestionsRequest inlineSuggestionsRequest) {
+ if (mClientSuggestionsSession == null) {
+ mClientSuggestionsSession = new ClientSuggestionsSession(id, mClient, mHandler,
+ mComponentName, this);
+ }
+
+ if (mContexts == null) {
+ mContexts = new ArrayList<>(1);
+ }
+
+ if (inlineSuggestionsRequest != null && !inlineSuggestionsRequest.isClientSupported()) {
+ inlineSuggestionsRequest = null;
+ }
+
+ mClientSuggestionsSession.onFillRequest(requestId, inlineSuggestionsRequest, mFlags);
+ }
+
/**
* The result of checking whether to show the save dialog, when session can be saved.
*
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 9ee0159..1a5d91c 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -4369,7 +4369,7 @@
return OperationType.BACKUP;
}
- long oldCallingId = Binder.clearCallingIdentity();
+ final long oldCallingId = Binder.clearCallingIdentity();
try {
IBackupTransport transport = transportClient.connectOrThrow(
/* caller */ "BMS.getOperationTypeFromTransport");
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 83dfe8e..2f5cc75 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -137,6 +137,8 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -145,6 +147,7 @@
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;
@@ -182,6 +185,11 @@
private static final String XML_ATTR_TIME_APPROVED = "time_approved";
private static final String XML_FILE_NAME = "companion_device_manager_associations.xml";
+ private static DateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ static {
+ sDateFormat.setTimeZone(TimeZone.getDefault());
+ }
+
private final CompanionDeviceManagerImpl mImpl;
private final ConcurrentMap<Integer, AtomicFile> mUidToStorage = new ConcurrentHashMap<>();
private PowerWhitelistManager mPowerWhitelistManager;
@@ -647,7 +655,7 @@
association.getDeviceMacAddress(),
association.getPackageName(),
association.getDeviceProfile(),
- active, /* notifyOnDeviceNearby */
+ active /* notifyOnDeviceNearby */,
association.getTimeApprovedMs());
} else {
return association;
@@ -726,12 +734,40 @@
synchronized (mLock) {
for (UserInfo user : getAllUsers()) {
forEach(mCachedAssociations.get(user.id), a -> {
- fout.append(" ")
- .append("u").append("" + a.getUserId()).append(": ")
- .append(a.getPackageName()).append(" - ")
- .append(a.getDeviceMacAddress()).append('\n');
+ fout.append(" ").append(a.toString()).append('\n');
});
}
+
+ }
+ fout.append("Currently Connected Devices:").append('\n');
+ for (int i = 0, size = mCurrentlyConnectedDevices.size(); i < size; i++) {
+ fout.append(" ").append(mCurrentlyConnectedDevices.get(i)).append('\n');
+ }
+
+ fout.append("Devices Last Nearby:").append('\n');
+ for (int i = 0, size = mDevicesLastNearby.size(); i < size; i++) {
+ String device = mDevicesLastNearby.keyAt(i);
+ Date time = mDevicesLastNearby.valueAt(i);
+ fout.append(" ").append(device).append(" -> ")
+ .append(sDateFormat.format(time)).append('\n');
+ }
+
+ fout.append("Discovery Service State:").append('\n');
+ for (int i = 0, size = mServiceConnectors.size(); i < size; i++) {
+ int userId = mServiceConnectors.keyAt(i);
+ fout.append(" ")
+ .append("u").append(Integer.toString(userId)).append(": ")
+ .append(Objects.toString(mServiceConnectors.valueAt(i)))
+ .append('\n');
+ }
+
+ fout.append("Device Listener Services State:").append('\n');
+ for (int i = 0, size = mDeviceListenerServiceConnectors.size(); i < size; i++) {
+ int userId = mDeviceListenerServiceConnectors.keyAt(i);
+ fout.append(" ")
+ .append("u").append(Integer.toString(userId)).append(": ")
+ .append(Objects.toString(mDeviceListenerServiceConnectors.valueAt(i)))
+ .append('\n');
}
}
}
@@ -772,7 +808,7 @@
+ " for " + association
+ " - profile still present in " + otherAssociationWithDeviceProfile);
} else {
- long identity = Binder.clearCallingIdentity();
+ final long identity = Binder.clearCallingIdentity();
try {
mRoleManager.removeRoleHolderAsUser(
association.getDeviceProfile(),
@@ -910,7 +946,7 @@
.getStringArray(com.android.internal.R.array.config_companionDeviceCerts);
Signature[] signatures = mPackageManagerInternal
- .getPackage(packageName).getSigningDetails().signatures;
+ .getPackage(packageName).getSigningDetails().getSignatures();
String[] apkCerts = PackageUtils.computeSignaturesSha256Digests(signatures);
Set<String> sameOemPackageCerts =
@@ -1052,7 +1088,7 @@
}
private List<UserInfo> getAllUsers() {
- long identity = Binder.clearCallingIdentity();
+ final long identity = Binder.clearCallingIdentity();
try {
return mUserManager.getUsers();
} finally {
@@ -1068,7 +1104,7 @@
}
private Set<Association> getAllAssociations() {
- long identity = Binder.clearCallingIdentity();
+ final long identity = Binder.clearCallingIdentity();
try {
ArraySet<Association> result = new ArraySet<>();
for (UserInfo user : mUserManager.getAliveUsers()) {
@@ -1080,6 +1116,7 @@
}
}
+
private Set<Association> getAllAssociations(
int userId, @Nullable String packageFilter, @Nullable String addressFilter) {
return CollectionUtils.filter(
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 55b982b..ec21d45 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -152,7 +152,7 @@
"android.hardware.configstore-V1.0-java",
"android.hardware.contexthub-V1.0-java",
"android.hardware.rebootescrow-V1-java",
- "android.hardware.soundtrigger-V2.3-java",
+ "android.hardware.soundtrigger-V2.4-java",
"android.hardware.power.stats-V1-java",
"android.hidl.manager-V1.2-java",
"capture_state_listener-aidl-java",
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 9299624..ca8df2c 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -30,6 +30,7 @@
import android.content.pm.PackageManager.ComponentInfoFlags;
import android.content.pm.PackageManager.PackageInfoFlags;
import android.content.pm.PackageManager.ResolveInfoFlags;
+import android.content.pm.SigningDetails.CertCapabilities;
import android.content.pm.overlay.OverlayPaths;
import android.content.pm.parsing.component.ParsedMainComponent;
import android.os.Bundle;
@@ -69,7 +70,6 @@
PACKAGE_BROWSER,
PACKAGE_SYSTEM_TEXT_CLASSIFIER,
PACKAGE_PERMISSION_CONTROLLER,
- PACKAGE_DOCUMENTER,
PACKAGE_CONFIGURATOR,
PACKAGE_INCIDENT_REPORT_APPROVER,
PACKAGE_APP_PREDICTOR,
@@ -730,7 +730,7 @@
* signing history for {@code serverUid} and with the {@code capability} specified.
*/
public abstract boolean hasSignatureCapability(int serverUid, int clientUid,
- @PackageParser.SigningDetails.CertCapabilities int capability);
+ @CertCapabilities int capability);
/**
* Get appIds of all available apps which specified android:sharedUserId in the manifest.
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 85ff2be..b4912bb 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -614,8 +614,12 @@
}
// waive WRITE_SECURE_SETTINGS permission check
final long callingIdentity = Binder.clearCallingIdentity();
- Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.BLUETOOTH_ON, value);
- Binder.restoreCallingIdentity(callingIdentity);
+ try {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.BLUETOOTH_ON, value);
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
}
/**
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index 71609d2..c0c347b 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -336,7 +336,7 @@
return;
}
- long token = Binder.clearCallingIdentity();
+ final long token = Binder.clearCallingIdentity();
try {
onSensorUseStarted(uid, packageName, sensor);
} finally {
@@ -608,7 +608,7 @@
mIndividualEnabled.put(userId, userIndividualEnabled);
if (!enable) {
- long token = Binder.clearCallingIdentity();
+ final long token = Binder.clearCallingIdentity();
try {
// Remove any notifications prompting the user to disable sensory privacy
NotificationManager notificationManager =
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 69c2926..ec2f4aa 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -455,12 +455,6 @@
"(?i)(^/storage/[^/]+/(?:([0-9]+)/)?Android/(?:data|media|obb|sandbox)/)([^/]+)(/.*)?");
- /** Automotive device unlockes users before system boot complete and this requires special
- * handling as vold reset can lead into race conditions. When this is set, all users unlocked
- * in {@code UserManager} level are unlocked after vold reset.
- */
- private final boolean mIsAutomotive;
-
private VolumeInfo findVolumeByIdOrThrow(String id) {
synchronized (mLock) {
final VolumeInfo vol = mVolumes.get(id);
@@ -1133,9 +1127,7 @@
mVold.onUserStarted(userId);
mStoraged.onUserStarted(userId);
}
- if (mIsAutomotive) {
- restoreSystemUnlockedUsers(userManager, users, systemUnlockedUsers);
- }
+ restoreSystemUnlockedUsers(userManager, users, systemUnlockedUsers);
mVold.onSecureKeyguardStateChanged(mSecureKeyguardShowing);
mStorageManagerInternal.onReset(mVold);
} catch (Exception e) {
@@ -1219,13 +1211,11 @@
// Record user as started so newly mounted volumes kick off events
// correctly, then synthesize events for any already-mounted volumes.
synchronized (mLock) {
- if (mIsAutomotive) {
- for (int unlockedUser : mSystemUnlockedUsers) {
- if (unlockedUser == userId) {
- // This can happen as restoreAllUnlockedUsers can double post the message.
- Log.i(TAG, "completeUnlockUser called for already unlocked user:" + userId);
- return;
- }
+ for (int unlockedUser : mSystemUnlockedUsers) {
+ if (unlockedUser == userId) {
+ // This can happen as restoreAllUnlockedUsers can double post the message.
+ Log.i(TAG, "completeUnlockUser called for already unlocked user:" + userId);
+ return;
}
}
for (int i = 0; i < mVolumes.size(); i++) {
@@ -1922,9 +1912,6 @@
if (WATCHDOG_ENABLE) {
Watchdog.getInstance().addMonitor(this);
}
-
- mIsAutomotive = context.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_AUTOMOTIVE);
}
private void start() {
@@ -3461,48 +3448,6 @@
}
}
- @Override
- public void notifyAppIoBlocked(String volumeUuid, int uid, int tid,
- @StorageManager.AppIoBlockedReason int reason) {
- enforceExternalStorageService();
-
- mStorageSessionController.notifyAppIoBlocked(volumeUuid, uid, tid, reason);
- }
-
- @Override
- public void notifyAppIoResumed(String volumeUuid, int uid, int tid,
- @StorageManager.AppIoBlockedReason int reason) {
- enforceExternalStorageService();
-
- mStorageSessionController.notifyAppIoResumed(volumeUuid, uid, tid, reason);
- }
-
- @Override
- public boolean isAppIoBlocked(String volumeUuid, int uid, int tid,
- @StorageManager.AppIoBlockedReason int reason) {
- return isAppIoBlocked(uid);
- }
-
-
- private boolean isAppIoBlocked(int uid) {
- return mStorageSessionController.isAppIoBlocked(uid);
- }
-
- /**
- * Enforces that the caller is the {@link ExternalStorageService}
- *
- * @throws SecurityException if the caller doesn't have the
- * {@link android.Manifest.permission.WRITE_MEDIA_STORAGE} permission or is not the
- * {@link ExternalStorageService}
- */
- private void enforceExternalStorageService() {
- enforcePermission(android.Manifest.permission.WRITE_MEDIA_STORAGE);
- int callingAppId = UserHandle.getAppId(Binder.getCallingUid());
- if (callingAppId != mMediaStoreAuthorityAppId) {
- throw new SecurityException("Only the ExternalStorageService is permitted");
- }
- }
-
/**
* Returns PendingIntent which can be used by Apps with MANAGE_EXTERNAL_STORAGE permission
* to launch the manageSpaceActivity of the App specified by packageName.
@@ -3517,7 +3462,7 @@
// We want to call the manageSpaceActivity as a SystemService and clear identity
// of the calling App
int originalUid = Binder.getCallingUidOrThrow();
- long token = Binder.clearCallingIdentity();
+ final long token = Binder.clearCallingIdentity();
try {
ApplicationInfo appInfo = mIPackageManager.getApplicationInfo(packageName, 0,
@@ -3550,6 +3495,46 @@
Binder.restoreCallingIdentity(token);
}
}
+
+ @Override
+ public void notifyAppIoBlocked(String volumeUuid, int uid, int tid, int reason) {
+ enforceExternalStorageService();
+
+ mStorageSessionController.notifyAppIoBlocked(volumeUuid, uid, tid, reason);
+ }
+
+ @Override
+ public void notifyAppIoResumed(String volumeUuid, int uid, int tid, int reason) {
+ enforceExternalStorageService();
+
+ mStorageSessionController.notifyAppIoResumed(volumeUuid, uid, tid, reason);
+ }
+
+ @Override
+ public boolean isAppIoBlocked(String volumeUuid, int uid, int tid,
+ @StorageManager.AppIoBlockedReason int reason) {
+ return isAppIoBlocked(uid);
+ }
+
+
+ private boolean isAppIoBlocked(int uid) {
+ return mStorageSessionController.isAppIoBlocked(uid);
+ }
+
+ /**
+ * Enforces that the caller is the {@link ExternalStorageService}
+ *
+ * @throws SecurityException if the caller doesn't have the
+ * {@link android.Manifest.permission.WRITE_MEDIA_STORAGE} permission or is not the
+ * {@link ExternalStorageService}
+ */
+ private void enforceExternalStorageService() {
+ enforcePermission(android.Manifest.permission.WRITE_MEDIA_STORAGE);
+ int callingAppId = UserHandle.getAppId(Binder.getCallingUid());
+ if (callingAppId != mMediaStoreAuthorityAppId) {
+ throw new SecurityException("Only the ExternalStorageService is permitted");
+ }
+ }
/** Not thread safe */
class AppFuseMountScope extends AppFuseBridge.MountScope {
@@ -4599,7 +4584,6 @@
pw.println();
pw.println("Local unlocked users: " + mLocalUnlockedUsers);
pw.println("System unlocked users: " + Arrays.toString(mSystemUnlockedUsers));
- pw.println("isAutomotive:" + mIsAutomotive);
}
synchronized (mObbMounts) {
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index a231de3..9575696 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -60,11 +60,11 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
import android.content.pm.RegisteredServicesCache;
import android.content.pm.RegisteredServicesCacheListener;
import android.content.pm.ResolveInfo;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails.CertCapabilities;
import android.content.pm.UserInfo;
import android.database.Cursor;
import android.database.sqlite.SQLiteStatement;
@@ -1830,6 +1830,11 @@
+ ", skipping since the account already exists");
return false;
}
+ if (accounts.accountsDb.findAllDeAccounts().size() > 100) {
+ Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString()
+ + ", skipping since more than 50 accounts on device exist");
+ return false;
+ }
long accountId = accounts.accountsDb.insertCeAccount(account, password);
if (accountId < 0) {
Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString()
@@ -4859,9 +4864,7 @@
int targetUid = targetActivityInfo.applicationInfo.uid;
PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
if (!isExportedSystemActivity(targetActivityInfo)
- && !pmi.hasSignatureCapability(
- targetUid, authUid,
- PackageParser.SigningDetails.CertCapabilities.AUTH)) {
+ && !pmi.hasSignatureCapability(targetUid, authUid, CertCapabilities.AUTH)) {
String pkgName = targetActivityInfo.packageName;
String activityName = targetActivityInfo.name;
String tmpl = "KEY_INTENT resolved to an Activity (%s) in a package (%s) that "
@@ -5614,8 +5617,7 @@
return SIGNATURE_CHECK_UID_MATCH;
}
if (pmi.hasSignatureCapability(
- serviceInfo.uid, callingUid,
- PackageParser.SigningDetails.CertCapabilities.AUTH)) {
+ serviceInfo.uid, callingUid, CertCapabilities.AUTH)) {
return SIGNATURE_CHECK_MATCH;
}
}
@@ -5656,8 +5658,7 @@
for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
serviceInfos) {
if (isOtherwisePermitted || pmi.hasSignatureCapability(
- serviceInfo.uid, callingUid,
- PackageParser.SigningDetails.CertCapabilities.AUTH)) {
+ serviceInfo.uid, callingUid, CertCapabilities.AUTH)) {
managedAccountTypes.add(serviceInfo.type.type);
}
}
diff --git a/services/core/java/com/android/server/accounts/OWNERS b/services/core/java/com/android/server/accounts/OWNERS
index 8dcc04a..df1b4f4 100644
--- a/services/core/java/com/android/server/accounts/OWNERS
+++ b/services/core/java/com/android/server/accounts/OWNERS
@@ -1,9 +1 @@
-carlosvaldivia@google.com
-dementyev@google.com
-sandrakwan@google.com
-hackbod@google.com
-svetoslavganov@google.com
-fkupolov@google.com
-yamasani@google.com
-omakoto@google.com
-
+include /core/java/android/accounts/OWNERS
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2de091b..d03c314 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3290,13 +3290,19 @@
final int max = SystemProperties.getInt("tombstoned.max_anr_count", 64);
final long now = System.currentTimeMillis();
- Arrays.sort(files, Comparator.comparingLong(File::lastModified).reversed());
- for (int i = 0; i < files.length; ++i) {
- if (i > max || (now - files[i].lastModified()) > DAY_IN_MILLIS) {
- if (!files[i].delete()) {
- Slog.w(TAG, "Unable to prune stale trace file: " + files[i]);
+ try {
+ Arrays.sort(files, Comparator.comparingLong(File::lastModified).reversed());
+ for (int i = 0; i < files.length; ++i) {
+ if (i > max || (now - files[i].lastModified()) > DAY_IN_MILLIS) {
+ if (!files[i].delete()) {
+ Slog.w(TAG, "Unable to prune stale trace file: " + files[i]);
+ }
}
}
+ } catch (IllegalArgumentException e) {
+ // The modification times changed while we were sorting. Bail...
+ // https://issuetracker.google.com/169836837
+ Slog.w(TAG, "tombstone modification times changed while sorting; not pruning", e);
}
}
@@ -8331,11 +8337,13 @@
if (lines > 0) {
sb.append("\n");
- // Merge several logcat streams, and take the last N lines
InputStreamReader input = null;
try {
java.lang.Process logcat = new ProcessBuilder(
- "/system/bin/timeout", "-k", "15s", "10s",
+ // Time out after 10s, but kill logcat with SEGV
+ // so we can investigate why it didn't finish.
+ "/system/bin/timeout", "-s", "SEGV", "10s",
+ // Merge several logcat streams, and take the last N lines.
"/system/bin/logcat", "-v", "threadtime", "-b", "events", "-b", "system",
"-b", "main", "-b", "crash", "-t", String.valueOf(lines))
.redirectErrorStream(true).start();
@@ -12074,6 +12082,31 @@
Slog.w(TAG, "Unable to bind backup agent for " + packageName);
return false;
}
+ if (app.backupAgentName != null) {
+ final ComponentName backupAgentName = new ComponentName(
+ app.packageName, app.backupAgentName);
+ int enableState = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+ try {
+ enableState = pm.getComponentEnabledSetting(backupAgentName, instantiatedUserId);
+ } catch (RemoteException e) {
+ // can't happen; package manager is process-local
+ }
+ switch (enableState) {
+ case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
+ case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER:
+ case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED:
+ Slog.w(TAG, "Unable to bind backup agent for " + backupAgentName
+ + ", the backup agent component is disabled.");
+ return false;
+
+ case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
+ case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
+ default:
+ // Since there's no way to declare a backup agent disabled in the manifest,
+ // assume the case COMPONENT_ENABLED_STATE_DEFAULT to be enabled.
+ break;
+ }
+ }
int oldBackupUid;
int newBackupUid;
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 4e6e91a..4d7719e 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -78,6 +78,7 @@
import com.android.internal.os.BatteryStatsHelper;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.BatteryUsageStatsProvider;
+import com.android.internal.os.BatteryUsageStatsStore;
import com.android.internal.os.BinderCallsStats;
import com.android.internal.os.PowerProfile;
import com.android.internal.os.RailStats;
@@ -124,10 +125,12 @@
Watchdog.Monitor {
static final String TAG = "BatteryStatsService";
static final boolean DBG = false;
+ private static final boolean BATTERY_USAGE_STORE_ENABLED = true;
private static IBatteryStats sService;
final BatteryStatsImpl mStats;
+ private final BatteryUsageStatsStore mBatteryUsageStatsStore;
private final BatteryStatsImpl.UserInfoProvider mUserManagerUserInfoProvider;
private final Context mContext;
private final BatteryExternalStatsWorker mWorker;
@@ -348,7 +351,14 @@
mStats.setPowerProfileLocked(new PowerProfile(context));
mStats.startTrackingSystemServerCpuTime();
- mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mStats);
+ if (BATTERY_USAGE_STORE_ENABLED) {
+ mBatteryUsageStatsStore =
+ new BatteryUsageStatsStore(context, mStats, systemDir, mHandler);
+ } else {
+ mBatteryUsageStatsStore = null;
+ }
+ mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mStats,
+ mBatteryUsageStatsStore);
}
public void publish() {
@@ -471,9 +481,9 @@
final PowerManagerInternal powerMgr = LocalServices.getService(PowerManagerInternal.class);
powerMgr.registerLowPowerModeObserver(this);
synchronized (mStats) {
- mStats.notePowerSaveModeLocked(
+ mStats.notePowerSaveModeLockedInit(
powerMgr.getLowPowerState(ServiceType.BATTERY_STATS).batterySaverEnabled,
- SystemClock.elapsedRealtime(), SystemClock.uptimeMillis(), true);
+ SystemClock.elapsedRealtime(), SystemClock.uptimeMillis());
}
(new WakeupReasonThread()).start();
}
@@ -516,7 +526,7 @@
mHandler.post(() -> {
synchronized (mStats) {
mStats.notePowerSaveModeLocked(result.batterySaverEnabled,
- elapsedRealtime, uptime, false);
+ elapsedRealtime, uptime);
}
});
}
@@ -752,6 +762,10 @@
FrameworkStatsLog.BATTERY_USAGE_STATS_SINCE_RESET_USING_POWER_PROFILE_MODEL,
null, // use default PullAtomMetadata values
BackgroundThread.getExecutor(), pullAtomCallback);
+ statsManager.setPullAtomCallback(
+ FrameworkStatsLog.BATTERY_USAGE_STATS_BEFORE_RESET,
+ null, // use default PullAtomMetadata values
+ BackgroundThread.getExecutor(), pullAtomCallback);
}
/** StatsPullAtomCallback for pulling BatteryUsageStats data. */
@@ -768,6 +782,17 @@
new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build();
bus = getBatteryUsageStats(List.of(powerProfileQuery)).get(0);
break;
+ case FrameworkStatsLog.BATTERY_USAGE_STATS_BEFORE_RESET:
+ final long sessionStart = mBatteryUsageStatsStore
+ .getLastBatteryUsageStatsBeforeResetAtomPullTimestamp();
+ final long sessionEnd = mStats.getStartClockTime();
+ final BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder()
+ .aggregateSnapshots(sessionStart, sessionEnd)
+ .build();
+ bus = getBatteryUsageStats(List.of(query)).get(0);
+ mBatteryUsageStatsStore
+ .setLastBatteryUsageStatsBeforeResetAtomPullTimestamp(sessionEnd);
+ break;
default:
throw new UnsupportedOperationException("Unknown tagId=" + atomTag);
}
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index e022e97..52ab4c8 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -92,6 +92,7 @@
DeviceConfig.NAMESPACE_STATSD_NATIVE,
DeviceConfig.NAMESPACE_STATSD_NATIVE_BOOT,
DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+ DeviceConfig.NAMESPACE_SWCODEC_NATIVE,
DeviceConfig.NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT,
};
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 136916a..17cee67 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3603,7 +3603,7 @@
}
// The default volume group is the one hosted by default product strategy, i.e.
// supporting Default Attributes
- return getVolumeGroupIdForAttributesInt(AudioProductStrategy.sDefaultAttributes);
+ return getVolumeGroupIdForAttributesInt(AudioProductStrategy.getDefaultAttributes());
}
private int getVolumeGroupIdForAttributesInt(@NonNull AudioAttributes attributes) {
@@ -6356,7 +6356,7 @@
private void ensureValidAttributes(AudioVolumeGroup avg) {
boolean hasAtLeastOneValidAudioAttributes = avg.getAudioAttributes().stream()
- .anyMatch(aa -> !aa.equals(AudioProductStrategy.sDefaultAttributes));
+ .anyMatch(aa -> !aa.equals(AudioProductStrategy.getDefaultAttributes()));
if (!hasAtLeastOneValidAudioAttributes) {
throw new IllegalArgumentException("Volume Group " + avg.name()
+ " has no valid audio attributes");
@@ -6404,7 +6404,7 @@
private int mIndexMax;
private int mLegacyStreamType = AudioSystem.STREAM_DEFAULT;
private int mPublicStreamType = AudioSystem.STREAM_MUSIC;
- private AudioAttributes mAudioAttributes = AudioProductStrategy.sDefaultAttributes;
+ private AudioAttributes mAudioAttributes = AudioProductStrategy.getDefaultAttributes();
// No API in AudioSystem to get a device from strategy or from attributes.
// Need a valid public stream type to use current API getDeviceForStream
@@ -6417,8 +6417,9 @@
if (DEBUG_VOL) {
Log.v(TAG, "VolumeGroupState for " + avg.toString());
}
+ // mAudioAttributes is the default at this point
for (final AudioAttributes aa : avg.getAudioAttributes()) {
- if (!aa.equals(AudioProductStrategy.sDefaultAttributes)) {
+ if (!aa.equals(mAudioAttributes)) {
mAudioAttributes = aa;
break;
}
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 54abc63..70ad4c1 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
@@ -263,7 +263,7 @@
final boolean isKeyguard = Utils.isKeyguard(getContext(), opPackageName);
// Clear calling identity when checking LockPatternUtils for StrongAuth flags.
- long identity = Binder.clearCallingIdentity();
+ final long identity1 = Binder.clearCallingIdentity();
try {
if (isKeyguard && Utils.isUserEncryptedOrLockdown(mLockPatternUtils, userId)) {
// If this happens, something in KeyguardUpdateMonitor is wrong.
@@ -273,7 +273,7 @@
return;
}
} finally {
- Binder.restoreCallingIdentity(identity);
+ Binder.restoreCallingIdentity(identity1);
}
final boolean restricted = getContext().checkCallingPermission(MANAGE_FINGERPRINT)
@@ -297,11 +297,11 @@
provider.second.getSensorProperties(sensorId);
if (!isKeyguard && !Utils.isSettings(getContext(), opPackageName)
&& sensorProps != null && sensorProps.isAnyUdfpsType()) {
- identity = Binder.clearCallingIdentity();
+ final long identity2 = Binder.clearCallingIdentity();
try {
authenticateWithPrompt(operationId, sensorProps, userId, receiver);
} finally {
- Binder.restoreCallingIdentity(identity);
+ Binder.restoreCallingIdentity(identity2);
}
} else {
provider.second.scheduleAuthenticate(provider.first, token, operationId, userId,
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 35f2957..9f806af 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -43,6 +43,7 @@
// The display device does not manage these properties itself, they are set by
// the display manager service. The display device shouldn't really be looking at these.
private int mCurrentLayerStack = -1;
+ private int mCurrentFlags = 0;
private int mCurrentOrientation = -1;
private Rect mCurrentLayerStackRect;
private Rect mCurrentDisplayRect;
@@ -212,6 +213,19 @@
}
/**
+ * Sets the display flags while in a transaction.
+ *
+ * Valid display flags:
+ * {@link SurfaceControl#DISPLAY_RECEIVES_INPUT}
+ */
+ public final void setDisplayFlagsLocked(SurfaceControl.Transaction t, int flags) {
+ if (mCurrentFlags != flags) {
+ mCurrentFlags = flags;
+ t.setDisplayFlags(mDisplayToken, flags);
+ }
+ }
+
+ /**
* Sets the display projection while in a transaction.
*
* @param orientation defines the display's orientation
@@ -298,6 +312,7 @@
pw.println("mUniqueId=" + mUniqueId);
pw.println("mDisplayToken=" + mDisplayToken);
pw.println("mCurrentLayerStack=" + mCurrentLayerStack);
+ pw.println("mCurrentFlags=" + mCurrentFlags);
pw.println("mCurrentOrientation=" + mCurrentOrientation);
pw.println("mCurrentLayerStackRect=" + mCurrentLayerStackRect);
pw.println("mCurrentDisplayRect=" + mCurrentDisplayRect);
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 9acb4c8..5186744 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -16,6 +16,8 @@
package com.android.server.display;
+import static com.android.server.display.DisplayDeviceInfo.TOUCH_NONE;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -512,6 +514,11 @@
boolean isBlanked) {
// Set the layer stack.
device.setLayerStackLocked(t, isBlanked ? BLANK_LAYER_STACK : mLayerStack);
+ // Also inform whether the device is the same one sent to inputflinger for its layerstack.
+ // TODO(b/188914255): Remove once input can dispatch against device vs layerstack.
+ device.setDisplayFlagsLocked(t,
+ device.getDisplayDeviceInfoLocked().touch != TOUCH_NONE
+ ? SurfaceControl.DISPLAY_RECEIVES_INPUT : 0);
// Set the color mode and allowed display mode.
if (device == mPrimaryDisplayDevice) {
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 7fa0b21..851accc 100644
--- a/services/core/java/com/android/server/display/color/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java
@@ -162,7 +162,8 @@
private final ReduceBrightColorsTintController mReduceBrightColorsTintController =
new ReduceBrightColorsTintController();
- private final Handler mHandler;
+ @VisibleForTesting
+ final Handler mHandler;
private final AppSaturationController mAppSaturationController = new AppSaturationController();
@@ -404,13 +405,13 @@
// existing activated state. This ensures consistency of tint across the color mode change.
onDisplayColorModeChanged(getColorModeInternal());
+ final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
if (mNightDisplayTintController.isAvailable(getContext())) {
// Reset the activated state.
mNightDisplayTintController.setActivated(null);
// Prepare the night display color transformation matrix.
- mNightDisplayTintController
- .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix());
+ mNightDisplayTintController.setUp(getContext(), dtm.needsLinearColorMatrix());
mNightDisplayTintController
.setMatrix(mNightDisplayTintController.getColorTemperatureSetting());
@@ -432,8 +433,7 @@
}
if (mReduceBrightColorsTintController.isAvailable(getContext())) {
- mReduceBrightColorsTintController
- .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix());
+ mReduceBrightColorsTintController.setUp(getContext(), dtm.needsLinearColorMatrix());
onReduceBrightColorsStrengthLevelChanged();
final boolean reset = resetReduceBrightColors();
if (!reset) {
@@ -540,8 +540,8 @@
mDisplayWhiteBalanceTintController.cancelAnimator();
if (mNightDisplayTintController.isAvailable(getContext())) {
- mNightDisplayTintController
- .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode));
+ final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
+ mNightDisplayTintController.setUp(getContext(), dtm.needsLinearColorMatrix(mode));
mNightDisplayTintController
.setMatrix(mNightDisplayTintController.getColorTemperatureSetting());
}
@@ -735,10 +735,11 @@
@VisibleForTesting
void updateDisplayWhiteBalanceStatus() {
boolean oldActivated = mDisplayWhiteBalanceTintController.isActivated();
+ final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
mDisplayWhiteBalanceTintController.setActivated(isDisplayWhiteBalanceSettingEnabled()
&& !mNightDisplayTintController.isActivated()
&& !isAccessibilityEnabled()
- && DisplayTransformManager.needsLinearColorMatrix());
+ && dtm.needsLinearColorMatrix());
boolean activated = mDisplayWhiteBalanceTintController.isActivated();
if (mDisplayWhiteBalanceListener != null && oldActivated != activated) {
diff --git a/services/core/java/com/android/server/display/color/DisplayTransformManager.java b/services/core/java/com/android/server/display/color/DisplayTransformManager.java
index 5c68c51..0dba9e1 100644
--- a/services/core/java/com/android/server/display/color/DisplayTransformManager.java
+++ b/services/core/java/com/android/server/display/color/DisplayTransformManager.java
@@ -239,7 +239,7 @@
/**
* Return true when the color matrix works in linear space.
*/
- public static boolean needsLinearColorMatrix() {
+ public boolean needsLinearColorMatrix() {
return SystemProperties.getInt(PERSISTENT_PROPERTY_DISPLAY_COLOR,
DISPLAY_COLOR_UNMANAGED) != DISPLAY_COLOR_UNMANAGED;
}
@@ -247,7 +247,7 @@
/**
* Return true when the specified colorMode requires the color matrix to work in linear space.
*/
- public static boolean needsLinearColorMatrix(int colorMode) {
+ public boolean needsLinearColorMatrix(int colorMode) {
return colorMode != ColorDisplayManager.COLOR_MODE_SATURATED;
}
diff --git a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
index 8405bbe..d422d51 100644
--- a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
+++ b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
@@ -88,7 +88,7 @@
tv.updateActiveSource(current, "ActiveSourceHandler");
invokeCallback(HdmiControlManager.RESULT_SUCCESS);
} else {
- tv.startRoutingControl(newActive.physicalAddress, current.physicalAddress, true,
+ tv.startRoutingControl(newActive.physicalAddress, current.physicalAddress,
mCallback);
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
index fefe953..5de89c9 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
@@ -317,12 +317,25 @@
R.bool.config_cecHdmiCecVersion20_allowed,
R.bool.config_cecHdmiCecVersion20_default);
+ Setting routingControlControl = registerSetting(
+ HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL,
+ R.bool.config_cecRoutingControl_userConfigurable);
+ routingControlControl.registerValue(HdmiControlManager.ROUTING_CONTROL_ENABLED,
+ R.bool.config_cecRoutingControlEnabled_allowed,
+ R.bool.config_cecRoutingControlEnabled_default);
+ routingControlControl.registerValue(HdmiControlManager.ROUTING_CONTROL_DISABLED,
+ R.bool.config_cecRoutingControlDisabled_allowed,
+ R.bool.config_cecRoutingControlDisabled_default);
+
Setting powerControlMode = registerSetting(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
R.bool.config_cecPowerControlMode_userConfigurable);
powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_TV,
R.bool.config_cecPowerControlModeTv_allowed,
R.bool.config_cecPowerControlModeTv_default);
+ powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM,
+ R.bool.config_cecPowerControlModeTvAndAudioSystem_allowed,
+ R.bool.config_cecPowerControlModeTvAndAudioSystem_default);
powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST,
R.bool.config_cecPowerControlModeBroadcast_allowed,
R.bool.config_cecPowerControlModeBroadcast_default);
@@ -342,6 +355,16 @@
R.bool.config_cecPowerStateChangeOnActiveSourceLostStandbyNow_allowed,
R.bool.config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default);
+ Setting systemAudioControl = registerSetting(
+ HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL,
+ R.bool.config_cecSystemAudioControl_userConfigurable);
+ systemAudioControl.registerValue(HdmiControlManager.SYSTEM_AUDIO_CONTROL_ENABLED,
+ R.bool.config_cecSystemAudioControlEnabled_allowed,
+ R.bool.config_cecSystemAudioControlEnabled_default);
+ systemAudioControl.registerValue(HdmiControlManager.SYSTEM_AUDIO_CONTROL_DISABLED,
+ R.bool.config_cecSystemAudioControlDisabled_allowed,
+ R.bool.config_cecSystemAudioControlDisabled_default);
+
Setting systemAudioModeMuting = registerSetting(
HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
R.bool.config_cecSystemAudioModeMuting_userConfigurable);
@@ -498,12 +521,16 @@
return STORAGE_GLOBAL_SETTINGS;
case HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION:
return STORAGE_SHARED_PREFS;
+ case HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL:
+ return STORAGE_GLOBAL_SETTINGS;
case HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE:
return STORAGE_GLOBAL_SETTINGS;
case HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE:
return STORAGE_GLOBAL_SETTINGS;
case HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST:
return STORAGE_SHARED_PREFS;
+ case HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL:
+ return STORAGE_GLOBAL_SETTINGS;
case HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING:
return STORAGE_SHARED_PREFS;
case HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY:
@@ -535,12 +562,16 @@
return Global.HDMI_CONTROL_ENABLED;
case HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION:
return setting.getName();
+ case HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL:
+ return Global.HDMI_CEC_SWITCH_ENABLED;
case HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE:
return Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP;
case HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE:
return Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED;
case HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST:
return setting.getName();
+ case HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL:
+ return Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED;
case HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING:
return setting.getName();
case HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY:
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
index 23b5c14..698ee0b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
@@ -481,4 +481,113 @@
// return muting ? CEC_KEYCODE_MUTE_FUNCTION : CEC_KEYCODE_RESTORE_VOLUME_FUNCTION;
return CEC_KEYCODE_MUTE;
}
+
+ public static String getKeycodeType(byte keycode) {
+ switch (keycode) {
+ case CEC_KEYCODE_UP:
+ case CEC_KEYCODE_DOWN:
+ case CEC_KEYCODE_LEFT:
+ case CEC_KEYCODE_RIGHT:
+ case CEC_KEYCODE_RIGHT_UP:
+ case CEC_KEYCODE_RIGHT_DOWN:
+ case CEC_KEYCODE_LEFT_UP:
+ case CEC_KEYCODE_LEFT_DOWN:
+ case CEC_KEYCODE_PAGE_UP:
+ case CEC_KEYCODE_PAGE_DOWN:
+ case CEC_KEYCODE_EXIT:
+ return "Navigation";
+ case CEC_KEYCODE_ROOT_MENU:
+ case CEC_KEYCODE_SETUP_MENU:
+ case CEC_KEYCODE_CONTENTS_MENU:
+ case CEC_KEYCODE_FAVORITE_MENU:
+ case CEC_KEYCODE_MEDIA_TOP_MENU:
+ case CEC_KEYCODE_MEDIA_CONTEXT_SENSITIVE_MENU:
+ return "Menu";
+ case CEC_KEYCODE_VOLUME_UP:
+ return "Volume up";
+ case CEC_KEYCODE_VOLUME_DOWN:
+ return "Volume down";
+ case CEC_KEYCODE_MUTE:
+ return "Volume mute";
+ case CEC_KEYCODE_POWER:
+ return "Power";
+ case CEC_KEYCODE_POWER_TOGGLE_FUNCTION:
+ return "Power toggle";
+ case CEC_KEYCODE_POWER_OFF_FUNCTION:
+ return "Power off";
+ case CEC_KEYCODE_POWER_ON_FUNCTION:
+ return "Power on";
+ case CEC_KEYCODE_F1_BLUE:
+ case CEC_KEYCODE_F2_RED:
+ case CEC_KEYCODE_F3_GREEN:
+ case CEC_KEYCODE_F4_YELLOW:
+ case CEC_KEYCODE_F5:
+ return "Function key";
+ case CEC_KEYCODE_NEXT_FAVORITE:
+ case CEC_KEYCODE_CHANNEL_UP:
+ case CEC_KEYCODE_CHANNEL_DOWN:
+ case CEC_KEYCODE_PREVIOUS_CHANNEL:
+ return "Channel";
+ case CEC_KEYCODE_NUMBER_11:
+ case CEC_KEYCODE_NUMBER_12:
+ case CEC_KEYCODE_NUMBER_0_OR_NUMBER_10:
+ case CEC_KEYCODE_NUMBERS_1:
+ case CEC_KEYCODE_NUMBERS_2:
+ case CEC_KEYCODE_NUMBERS_3:
+ case CEC_KEYCODE_NUMBERS_4:
+ case CEC_KEYCODE_NUMBERS_5:
+ case CEC_KEYCODE_NUMBERS_6:
+ case CEC_KEYCODE_NUMBERS_7:
+ case CEC_KEYCODE_NUMBERS_8:
+ case CEC_KEYCODE_NUMBERS_9:
+ return "Number";
+ case CEC_KEYCODE_PLAY:
+ case CEC_KEYCODE_STOP:
+ case CEC_KEYCODE_PAUSE:
+ case CEC_KEYCODE_RECORD:
+ case CEC_KEYCODE_REWIND:
+ case CEC_KEYCODE_FAST_FORWARD:
+ case CEC_KEYCODE_EJECT:
+ case CEC_KEYCODE_FORWARD:
+ case CEC_KEYCODE_BACKWARD:
+ case CEC_KEYCODE_STOP_RECORD:
+ case CEC_KEYCODE_PAUSE_RECORD:
+ case CEC_KEYCODE_ANGLE:
+ case CEC_KEYCODE_SUB_PICTURE:
+ case CEC_KEYCODE_VIDEO_ON_DEMAND:
+ return "Media";
+ case CEC_KEYCODE_PLAY_FUNCTION:
+ case CEC_KEYCODE_PAUSE_PLAY_FUNCTION:
+ case CEC_KEYCODE_RECORD_FUNCTION:
+ case CEC_KEYCODE_PAUSE_RECORD_FUNCTION:
+ case CEC_KEYCODE_STOP_FUNCTION:
+ case CEC_KEYCODE_MUTE_FUNCTION:
+ case CEC_KEYCODE_RESTORE_VOLUME_FUNCTION:
+ case CEC_KEYCODE_TUNE_FUNCTION:
+ case CEC_KEYCODE_SELECT_MEDIA_FUNCTION:
+ case CEC_KEYCODE_SELECT_AV_INPUT_FUNCTION:
+ case CEC_KEYCODE_SELECT_AUDIO_INPUT_FUNCTION:
+ return "Functional";
+ case CEC_KEYCODE_ELECTRONIC_PROGRAM_GUIDE:
+ case CEC_KEYCODE_TIMER_PROGRAMMING:
+ return "Timer";
+ case CEC_KEYCODE_SOUND_SELECT:
+ case CEC_KEYCODE_SELECT_SOUND_PRESENTATION:
+ case CEC_KEYCODE_SELECT_BROADCAST_TYPE:
+ case CEC_KEYCODE_INPUT_SELECT:
+ case CEC_KEYCODE_SELECT:
+ return "Select";
+ case CEC_KEYCODE_NUMBER_ENTRY_MODE:
+ case CEC_KEYCODE_DOT:
+ case CEC_KEYCODE_CLEAR:
+ case CEC_KEYCODE_ENTER:
+ case CEC_KEYCODE_DISPLAY_INFORMATION:
+ case CEC_KEYCODE_HELP:
+ case CEC_KEYCODE_DATA:
+ case CEC_KEYCODE_INITIAL_CONFIGURATION:
+ return "General";
+ default:
+ return "Unknown";
+ }
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 0bb1285..93b0560 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -34,7 +34,6 @@
import android.media.tv.TvInputInfo;
import android.media.tv.TvInputManager.TvInputCallback;
import android.os.SystemProperties;
-import android.provider.Settings.Global;
import android.sysprop.HdmiProperties;
import android.util.Slog;
@@ -108,10 +107,12 @@
protected HdmiCecLocalDeviceAudioSystem(HdmiControlService service) {
super(service, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
- mRoutingControlFeatureEnabled =
- mService.readBooleanSetting(Global.HDMI_CEC_SWITCH_ENABLED, false);
- mSystemAudioControlFeatureEnabled =
- mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true);
+ mRoutingControlFeatureEnabled = mService.getHdmiCecConfig().getIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL)
+ == HdmiControlManager.ROUTING_CONTROL_ENABLED;
+ mSystemAudioControlFeatureEnabled = mService.getHdmiCecConfig().getIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL)
+ == HdmiControlManager.SYSTEM_AUDIO_CONTROL_ENABLED;
}
private static final String SHORT_AUDIO_DESCRIPTOR_CONFIG_PATH = "/vendor/etc/sadConfig.xml";
@@ -857,7 +858,7 @@
HdmiLogger.debug("[A]UpdateSystemAudio mode[on=%b] output=[%X]", on, device);
}
- void onSystemAduioControlFeatureSupportChanged(boolean enabled) {
+ void onSystemAudioControlFeatureSupportChanged(boolean enabled) {
setSystemAudioControlFeatureEnabled(enabled);
if (enabled) {
addAndStartAction(new SystemAudioInitiationActionFromAvr(this));
@@ -873,7 +874,7 @@
}
@ServiceThreadOnly
- void setRoutingControlFeatureEnables(boolean enabled) {
+ void setRoutingControlFeatureEnabled(boolean enabled) {
assertRunOnServiceThread();
synchronized (mLock) {
mRoutingControlFeatureEnabled = enabled;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 37ee76b..1276aa3 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -140,13 +140,10 @@
// Invalidate the internal active source record when going to standby
mService.setActiveSource(Constants.ADDR_INVALID, Constants.INVALID_PHYSICAL_ADDRESS,
"HdmiCecLocalDevicePlayback#onStandby()");
- boolean mTvSendStandbyOnSleep = mService.getHdmiCecConfig().getIntValue(
- HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP)
- == HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED;
if (!wasActiveSource) {
return;
}
- if (initiatedByCec || !mTvSendStandbyOnSleep) {
+ if (initiatedByCec) {
mService.sendCecCommand(HdmiCecMessageBuilder.buildInactiveSource(mAddress,
mService.getPhysicalAddress()));
return;
@@ -155,13 +152,20 @@
case HdmiControlService.STANDBY_SCREEN_OFF:
// Get latest setting value
@HdmiControlManager.PowerControlMode
- String sendStandbyOnSleep = mService.getHdmiCecConfig().getStringValue(
+ String powerControlMode = mService.getHdmiCecConfig().getStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE);
- switch (sendStandbyOnSleep) {
+ switch (powerControlMode) {
case HdmiControlManager.POWER_CONTROL_MODE_TV:
mService.sendCecCommand(
HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_TV));
break;
+ case HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM:
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_TV));
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildStandby(mAddress,
+ Constants.ADDR_AUDIO_SYSTEM));
+ break;
case HdmiControlManager.POWER_CONTROL_MODE_BROADCAST:
mService.sendCecCommand(
HdmiCecMessageBuilder.buildStandby(mAddress,
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
index 1c726e0..0c7b3f6 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
@@ -107,14 +107,18 @@
@ServiceThreadOnly
protected void sendStandby(int deviceId) {
assertRunOnServiceThread();
- String sendStandbyOnSleep = mService.getHdmiCecConfig().getStringValue(
+ String powerControlMode = mService.getHdmiCecConfig().getStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE);
- if (sendStandbyOnSleep.equals(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST)) {
+ if (powerControlMode.equals(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST)) {
mService.sendCecCommand(
HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_BROADCAST));
return;
}
mService.sendCecCommand(HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_TV));
+ if (powerControlMode.equals(HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM)) {
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_AUDIO_SYSTEM));
+ }
}
@ServiceThreadOnly
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index acfeb6c..a887463 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -42,7 +42,6 @@
import android.media.AudioSystem;
import android.media.tv.TvInputInfo;
import android.media.tv.TvInputManager.TvInputCallback;
-import android.provider.Settings.Global;
import android.util.Slog;
import android.util.SparseBooleanArray;
@@ -155,8 +154,9 @@
HdmiCecLocalDeviceTv(HdmiControlService service) {
super(service, HdmiDeviceInfo.DEVICE_TV);
mPrevPortId = Constants.INVALID_PORT_ID;
- mSystemAudioControlFeatureEnabled =
- mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true);
+ mSystemAudioControlFeatureEnabled = service.getHdmiCecConfig().getIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL)
+ == HdmiControlManager.SYSTEM_AUDIO_CONTROL_ENABLED;
mStandbyHandler = new HdmiCecStandbyModeHandler(service, this);
}
@@ -374,12 +374,11 @@
return;
}
int newPath = mService.portIdToPath(portId);
- startRoutingControl(oldPath, newPath, true, callback);
+ startRoutingControl(oldPath, newPath, callback);
}
@ServiceThreadOnly
- void startRoutingControl(int oldPath, int newPath, boolean queryDevicePowerStatus,
- IHdmiControlCallback callback) {
+ void startRoutingControl(int oldPath, int newPath, IHdmiControlCallback callback) {
assertRunOnServiceThread();
if (oldPath == newPath) {
return;
@@ -389,7 +388,7 @@
mService.sendCecCommand(routingChange);
removeAction(RoutingControlAction.class);
addAndStartAction(
- new RoutingControlAction(this, newPath, queryDevicePowerStatus, callback));
+ new RoutingControlAction(this, newPath, callback));
}
@ServiceThreadOnly
@@ -567,7 +566,7 @@
if (isTailOfActivePath(path, getActivePath())) {
int newPath = mService.portIdToPath(getActivePortId());
setActivePath(newPath);
- startRoutingControl(getActivePath(), newPath, false, null);
+ startRoutingControl(getActivePath(), newPath, null);
return true;
}
return false;
@@ -612,7 +611,7 @@
getActiveSource().invalidate();
removeAction(RoutingControlAction.class);
int newPath = HdmiUtils.twoBytesToInt(params, 2);
- addAndStartAction(new RoutingControlAction(this, newPath, true, null));
+ addAndStartAction(new RoutingControlAction(this, newPath, null));
}
return Constants.HANDLED;
}
@@ -1189,7 +1188,7 @@
// Seq #23
if (isTailOfActivePath(path, getActivePath())) {
int newPath = mService.portIdToPath(getActivePortId());
- startRoutingControl(getActivePath(), newPath, true, null);
+ startRoutingControl(getActivePath(), newPath, null);
}
}
@@ -1207,7 +1206,7 @@
if (!routingForBootup && !isProhibitMode()) {
int newPath = mService.portIdToPath(getActivePortId());
setActivePath(newPath);
- startRoutingControl(getActivePath(), newPath, routingForBootup, null);
+ startRoutingControl(getActivePath(), newPath, null);
}
} else {
int activePath = mService.getPhysicalAddress();
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
index c85fd50..15a3167 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
@@ -119,6 +119,10 @@
if (mParams.length > 0) {
if (filterMessageParameters(mOpcode)) {
s.append(String.format(" <Redacted len=%d>", mParams.length));
+ } else if (isUserControlPressedMessage(mOpcode)) {
+ s.append(
+ String.format(
+ " <Keycode type = %s>", HdmiCecKeycode.getKeycodeType(mParams[0])));
} else {
for (byte data : mParams) {
s.append(String.format(":%02X", data));
@@ -287,7 +291,6 @@
private static boolean filterMessageParameters(int opcode) {
switch (opcode) {
- case Constants.MESSAGE_USER_CONTROL_PRESSED:
case Constants.MESSAGE_USER_CONTROL_RELEASED:
case Constants.MESSAGE_SET_OSD_NAME:
case Constants.MESSAGE_SET_OSD_STRING:
@@ -300,5 +303,9 @@
return false;
}
}
+
+ private static boolean isUserControlPressedMessage(int opcode) {
+ return Constants.MESSAGE_USER_CONTROL_PRESSED == opcode;
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 2ed160a..eb0363c 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -618,6 +618,53 @@
initializeCec(INITIATED_BY_ENABLE_CEC);
}
}, mServiceThreadExecutor);
+ mHdmiCecConfig.registerChangeListener(HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL,
+ new HdmiCecConfig.SettingChangeListener() {
+ @Override
+ public void onChange(String setting) {
+ boolean enabled = mHdmiCecConfig.getIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL)
+ == HdmiControlManager.ROUTING_CONTROL_ENABLED;
+ if (isAudioSystemDevice()) {
+ if (audioSystem() == null) {
+ Slog.w(TAG, "Switch device has not registered yet."
+ + " Can't turn routing on.");
+ } else {
+ audioSystem().setRoutingControlFeatureEnabled(enabled);
+ }
+ }
+ }
+ }, mServiceThreadExecutor);
+ mHdmiCecConfig.registerChangeListener(
+ HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL,
+ new HdmiCecConfig.SettingChangeListener() {
+ @Override
+ public void onChange(String setting) {
+ boolean enabled = mHdmiCecConfig.getIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL)
+ == HdmiControlManager.SYSTEM_AUDIO_CONTROL_ENABLED;
+ if (isTvDeviceEnabled()) {
+ tv().setSystemAudioControlFeatureEnabled(enabled);
+ }
+ if (isAudioSystemDevice()) {
+ if (audioSystem() == null) {
+ Slog.e(TAG, "Audio System device has not registered yet."
+ + " Can't turn system audio mode on.");
+ } else {
+ audioSystem().onSystemAudioControlFeatureSupportChanged(enabled);
+ }
+ }
+ }
+ }, mServiceThreadExecutor);
+ mHdmiCecConfig.registerChangeListener(
+ HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
+ new HdmiCecConfig.SettingChangeListener() {
+ @Override
+ public void onChange(String setting) {
+ setHdmiCecVolumeControlEnabledInternal(getHdmiCecConfig().getIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE));
+ }
+ }, mServiceThreadExecutor);
mHdmiCecConfig.registerChangeListener(
HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
new HdmiCecConfig.SettingChangeListener() {
@@ -757,11 +804,8 @@
private void registerContentObserver() {
ContentResolver resolver = getContext().getContentResolver();
String[] settings = new String[] {
- Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED,
- Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED,
Global.MHL_INPUT_SWITCHING_ENABLED,
Global.MHL_POWER_CHARGE_ENABLED,
- Global.HDMI_CEC_SWITCH_ENABLED,
Global.DEVICE_NAME
};
for (String s : settings) {
@@ -781,33 +825,6 @@
String option = uri.getLastPathSegment();
boolean enabled = readBooleanSetting(option, true);
switch (option) {
- case Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED:
- setHdmiCecVolumeControlEnabledInternal(getHdmiCecConfig().getIntValue(
- HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE));
- break;
- case Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED:
- if (isTvDeviceEnabled()) {
- tv().setSystemAudioControlFeatureEnabled(enabled);
- }
- if (isAudioSystemDevice()) {
- if (audioSystem() == null) {
- Slog.e(TAG, "Audio System device has not registered yet."
- + " Can't turn system audio mode on.");
- break;
- }
- audioSystem().onSystemAduioControlFeatureSupportChanged(enabled);
- }
- break;
- case Global.HDMI_CEC_SWITCH_ENABLED:
- if (isAudioSystemDevice()) {
- if (audioSystem() == null) {
- Slog.w(TAG, "Switch device has not registered yet."
- + " Can't turn routing on.");
- break;
- }
- audioSystem().setRoutingControlFeatureEnables(enabled);
- }
- break;
case Global.MHL_INPUT_SWITCHING_ENABLED:
setMhlInputChangeEnabled(enabled);
break;
@@ -2359,7 +2376,7 @@
@Override
public List<String> getUserCecSettings() {
initBinderCall();
- long token = Binder.clearCallingIdentity();
+ final long token = Binder.clearCallingIdentity();
try {
return HdmiControlService.this.getHdmiCecConfig().getUserSettings();
} finally {
@@ -2370,7 +2387,7 @@
@Override
public List<String> getAllowedCecSettingStringValues(String name) {
initBinderCall();
- long token = Binder.clearCallingIdentity();
+ final long token = Binder.clearCallingIdentity();
try {
return HdmiControlService.this.getHdmiCecConfig().getAllowedStringValues(name);
} finally {
@@ -2381,7 +2398,7 @@
@Override
public int[] getAllowedCecSettingIntValues(String name) {
initBinderCall();
- long token = Binder.clearCallingIdentity();
+ final long token = Binder.clearCallingIdentity();
try {
List<Integer> allowedValues =
HdmiControlService.this.getHdmiCecConfig().getAllowedIntValues(name);
@@ -2394,7 +2411,7 @@
@Override
public String getCecSettingStringValue(String name) {
initBinderCall();
- long token = Binder.clearCallingIdentity();
+ final long token = Binder.clearCallingIdentity();
try {
return HdmiControlService.this.getHdmiCecConfig().getStringValue(name);
} finally {
@@ -2405,7 +2422,7 @@
@Override
public void setCecSettingStringValue(String name, String value) {
initBinderCall();
- long token = Binder.clearCallingIdentity();
+ final long token = Binder.clearCallingIdentity();
try {
HdmiControlService.this.getHdmiCecConfig().setStringValue(name, value);
} finally {
@@ -2416,7 +2433,7 @@
@Override
public int getCecSettingIntValue(String name) {
initBinderCall();
- long token = Binder.clearCallingIdentity();
+ final long token = Binder.clearCallingIdentity();
try {
return HdmiControlService.this.getHdmiCecConfig().getIntValue(name);
} finally {
@@ -2427,7 +2444,7 @@
@Override
public void setCecSettingIntValue(String name, int value) {
initBinderCall();
- long token = Binder.clearCallingIdentity();
+ final long token = Binder.clearCallingIdentity();
try {
HdmiControlService.this.getHdmiCecConfig().setIntValue(name, value);
} finally {
diff --git a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
index 979e7a4..3dcf72e 100644
--- a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
+++ b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
@@ -178,10 +178,11 @@
if (service.isAudioSystemDevice()) {
return false;
}
- @HdmiControlManager.PowerControlMode String sendStandbyOnSleep =
+ @HdmiControlManager.PowerControlMode String powerControlMode =
service.getHdmiCecConfig().getStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE);
- return sendStandbyOnSleep.equals(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
+ return powerControlMode.equals(HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM)
+ || powerControlMode.equals(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
}
private static int getTargetCecVersion(HdmiCecLocalDevice localDevice,
diff --git a/services/core/java/com/android/server/hdmi/RoutingControlAction.java b/services/core/java/com/android/server/hdmi/RoutingControlAction.java
index b9404e4..0c4fb26 100644
--- a/services/core/java/com/android/server/hdmi/RoutingControlAction.java
+++ b/services/core/java/com/android/server/hdmi/RoutingControlAction.java
@@ -17,11 +17,10 @@
package com.android.server.hdmi;
import android.hardware.hdmi.HdmiControlManager;
-import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback;
import android.util.Slog;
-import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
+import com.android.internal.annotations.VisibleForTesting;
/**
* Feature action for routing control. Exchanges routing-related commands with other devices
@@ -41,23 +40,12 @@
// State in which we wait for <Routing Information> to arrive. If timed out, we use the
// latest routing path to set the new active source.
- private static final int STATE_WAIT_FOR_ROUTING_INFORMATION = 1;
-
- // State in which we wait for <Report Power Status> in response to <Give Device Power Status>
- // we have sent. If the response tells us the device power is on, we send <Set Stream Path>
- // to make it the active source. Otherwise we do not send <Set Stream Path>, and possibly
- // just show the blank screen.
- private static final int STATE_WAIT_FOR_REPORT_POWER_STATUS = 2;
+ @VisibleForTesting
+ static final int STATE_WAIT_FOR_ROUTING_INFORMATION = 1;
// Time out in millseconds used for <Routing Information>
private static final int TIMEOUT_ROUTING_INFORMATION_MS = 1000;
- // Time out in milliseconds used for <Report Power Status>
- private static final int TIMEOUT_REPORT_POWER_STATUS_MS = 1000;
-
- // true if <Give Power Status> should be sent once the new active routing path is determined.
- private final boolean mQueryDevicePowerStatus;
-
// If set to true, call {@link HdmiControlService#invokeInputChangeListener()} when
// the routing control/active source change happens. The listener should be called if
// the events are triggered by external events such as manual switch port change or incoming
@@ -67,11 +55,9 @@
// The latest routing path. Updated by each <Routing Information> from CEC switches.
private int mCurrentRoutingPath;
- RoutingControlAction(HdmiCecLocalDevice localDevice, int path, boolean queryDevicePowerStatus,
- IHdmiControlCallback callback) {
+ RoutingControlAction(HdmiCecLocalDevice localDevice, int path, IHdmiControlCallback callback) {
super(localDevice, callback);
mCurrentRoutingPath = path;
- mQueryDevicePowerStatus = queryDevicePowerStatus;
// Callback is non-null when routing control action is brought up by binder API. Use
// this as an indicator for the input change notification. These API calls will get
// the result through this callback, not through notification. Any other events that
@@ -104,39 +90,16 @@
removeActionExcept(RoutingControlAction.class, this);
addTimer(mState, TIMEOUT_ROUTING_INFORMATION_MS);
return true;
- } else if (mState == STATE_WAIT_FOR_REPORT_POWER_STATUS
- && opcode == Constants.MESSAGE_REPORT_POWER_STATUS) {
- handleReportPowerStatus(cmd.getParams()[0]);
- return true;
}
return false;
}
- private void handleReportPowerStatus(int devicePowerStatus) {
- if (isPowerOnOrTransient(getTvPowerStatus())) {
- updateActiveInput();
- if (isPowerOnOrTransient(devicePowerStatus)) {
- sendSetStreamPath();
- }
- }
- finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
- }
-
private void updateActiveInput() {
HdmiCecLocalDeviceTv tv = tv();
tv.setPrevPortId(tv.getActivePortId());
tv.updateActiveInput(mCurrentRoutingPath, mNotifyInputChange);
}
- private int getTvPowerStatus() {
- return tv().getPowerStatus();
- }
-
- private static boolean isPowerOnOrTransient(int status) {
- return status == HdmiControlManager.POWER_STATUS_ON
- || status == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
- }
-
private void sendSetStreamPath() {
sendCommand(HdmiCecMessageBuilder.buildSetStreamPath(getSourceAddress(),
mCurrentRoutingPath));
@@ -150,46 +113,13 @@
}
switch (timeoutState) {
case STATE_WAIT_FOR_ROUTING_INFORMATION:
- HdmiDeviceInfo device =
- localDevice().mService.getHdmiCecNetwork().getDeviceInfoByPath(
- mCurrentRoutingPath);
- if (device != null && mQueryDevicePowerStatus) {
- int deviceLogicalAddress = device.getLogicalAddress();
- queryDevicePowerStatus(deviceLogicalAddress, new SendMessageCallback() {
- @Override
- public void onSendCompleted(int error) {
- handlDevicePowerStatusAckResult(
- error == HdmiControlManager.RESULT_SUCCESS);
- }
- });
- } else {
- updateActiveInput();
- finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
- }
- return;
- case STATE_WAIT_FOR_REPORT_POWER_STATUS:
- if (isPowerOnOrTransient(getTvPowerStatus())) {
- updateActiveInput();
- sendSetStreamPath();
- }
+ updateActiveInput();
+ sendSetStreamPath();
finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
return;
- }
- }
-
- private void queryDevicePowerStatus(int address, SendMessageCallback callback) {
- sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(), address),
- callback);
- }
-
- private void handlDevicePowerStatusAckResult(boolean acked) {
- if (acked) {
- mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
- addTimer(mState, TIMEOUT_REPORT_POWER_STATUS_MS);
- } else {
- updateActiveInput();
- sendSetStreamPath();
- finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
+ default:
+ Slog.e("CEC", "Invalid timeoutState (" + timeoutState + ").");
+ return;
}
}
}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 9fcc9a1..d3083ca 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -2856,8 +2856,7 @@
};
for (File baseDir: baseDirs) {
File confFile = new File(baseDir, EXCLUDED_DEVICES_PATH);
- try {
- InputStream stream = new FileInputStream(confFile);
+ try (InputStream stream = new FileInputStream(confFile)) {
names.addAll(ConfigurationProcessor.processExcludedDeviceNames(stream));
} catch (FileNotFoundException e) {
// It's ok if the file does not exist.
@@ -2890,8 +2889,7 @@
final File baseDir = Environment.getVendorDirectory();
final File confFile = new File(baseDir, PORT_ASSOCIATIONS_PATH);
- try {
- final InputStream stream = new FileInputStream(confFile);
+ try (final InputStream stream = new FileInputStream(confFile)) {
return ConfigurationProcessor.processInputPortAssociations(stream);
} catch (FileNotFoundException e) {
// Most of the time, file will not exist, which is expected.
@@ -3012,10 +3010,10 @@
@Override
public void visitKeyboardLayout(Resources resources,
int keyboardLayoutResId, KeyboardLayout layout) {
- try {
+ try (final InputStreamReader stream = new InputStreamReader(
+ resources.openRawResource(keyboardLayoutResId))) {
result[0] = layout.getDescriptor();
- result[1] = Streams.readFully(new InputStreamReader(
- resources.openRawResource(keyboardLayoutResId)));
+ result[1] = Streams.readFully(stream);
} catch (IOException ex) {
} catch (NotFoundException ex) {
}
diff --git a/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java b/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java
index 1967e02..e375007 100644
--- a/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java
@@ -82,7 +82,7 @@
}
public void addListener(CallerIdentity callerIdentity, IGnssAntennaInfoListener listener) {
- long identity = Binder.clearCallingIdentity();
+ final long identity = Binder.clearCallingIdentity();
try {
putRegistration(listener.asBinder(),
new AntennaInfoListenerRegistration(callerIdentity, listener));
@@ -92,7 +92,7 @@
}
public void removeListener(IGnssAntennaInfoListener listener) {
- long identity = Binder.clearCallingIdentity();
+ final long identity = Binder.clearCallingIdentity();
try {
removeRegistration(listener.asBinder());
} finally {
diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
index 21946ca..5de9cf3 100644
--- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
@@ -331,7 +331,7 @@
@Override
public void onCapabilitiesChanged(GnssCapabilities oldCapabilities,
GnssCapabilities newCapabilities) {
- long ident = Binder.clearCallingIdentity();
+ final long ident = Binder.clearCallingIdentity();
try {
Intent intent = new Intent(LocationManager.ACTION_GNSS_CAPABILITIES_CHANGED)
.putExtra(LocationManager.EXTRA_GNSS_CAPABILITIES, newCapabilities)
diff --git a/services/core/java/com/android/server/location/injector/SystemDeviceStationaryHelper.java b/services/core/java/com/android/server/location/injector/SystemDeviceStationaryHelper.java
index 9874ecf..7369eac 100644
--- a/services/core/java/com/android/server/location/injector/SystemDeviceStationaryHelper.java
+++ b/services/core/java/com/android/server/location/injector/SystemDeviceStationaryHelper.java
@@ -41,7 +41,7 @@
public void addListener(DeviceIdleInternal.StationaryListener listener) {
Preconditions.checkState(mDeviceIdle != null);
- long identity = Binder.clearCallingIdentity();
+ final long identity = Binder.clearCallingIdentity();
try {
mDeviceIdle.registerStationaryListener(listener);
} finally {
@@ -53,7 +53,7 @@
public void removeListener(DeviceIdleInternal.StationaryListener listener) {
Preconditions.checkState(mDeviceIdle != null);
- long identity = Binder.clearCallingIdentity();
+ final long identity = Binder.clearCallingIdentity();
try {
mDeviceIdle.unregisterStationaryListener(listener);
} finally {
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 0bec09c..2efb239 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -1620,7 +1620,7 @@
+ PERMISSION);
}
- long identity = Binder.clearCallingIdentity();
+ final long identity = Binder.clearCallingIdentity();
try {
enforceFrpResolved();
// When changing credential for profiles with unified challenge, some callers
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
index 42b7c9d3..8a33299 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
@@ -354,6 +354,12 @@
}
}
+ public void unregisterFileCleanupReceiver() {
+ if(mContext != null) {
+ mContext.unregisterReceiver(mFileCleanupReceiver);
+ }
+ }
+
private static long safeParseLong(String fileName) {
// AtomicFile will create copies of the numeric files with ".new" and ".bak"
// over the course of its processing. If these files still exist on boot we need to clean
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryManager.java b/services/core/java/com/android/server/notification/NotificationHistoryManager.java
index 6da898a..0aacd13 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryManager.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryManager.java
@@ -288,6 +288,7 @@
private void disableHistory(NotificationHistoryDatabase userHistory, @UserIdInt int userId) {
userHistory.disableHistory();
+ userHistory.unregisterFileCleanupReceiver();
mUserPendingHistoryDisables.put(userId, false);
mHistoryEnabled.put(userId, false);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index b26485b..50e2d98 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2311,14 +2311,16 @@
mUserProfiles.updateCache(getContext());
- telephonyManager.listen(new PhoneStateListener() {
- @Override
- public void onCallStateChanged(int state, String incomingNumber) {
- if (mCallState == state) return;
- if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
- mCallState = state;
- }
- }, PhoneStateListener.LISTEN_CALL_STATE);
+ if (mPackageManagerClient.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ telephonyManager.listen(new PhoneStateListener() {
+ @Override
+ public void onCallStateChanged(int state, String incomingNumber) {
+ if (mCallState == state) return;
+ if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
+ mCallState = state;
+ }
+ }, PhoneStateListener.LISTEN_CALL_STATE);
+ }
mSettingsObserver = new SettingsObserver(mHandler);
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
index 0e2ff75..3ca6a0d 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
@@ -429,4 +429,14 @@
return NotificationChannelLogger.getLoggingImportance(channel, importance);
}
+ /**
+ * @param r NotificationRecord
+ * @return Whether the notification is a foreground service notification.
+ */
+ static boolean isForegroundService(@NonNull NotificationRecord r) {
+ if (r.getSbn() == null || r.getSbn().getNotification() == null) {
+ return false;
+ }
+ return (r.getSbn().getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
+ }
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
index 1a99fb0e..249910d 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
@@ -63,7 +63,12 @@
/* android.stats.sysui.NotificationImportance importance_asst = 19 */
r.getAssistantImportance(),
/* int32 assistant_hash = 20 */ p.getAssistantHash(),
- /* float assistant_ranking_score = 21 */ r.getRankingScore()
+ /* float assistant_ranking_score = 21 */ r.getRankingScore(),
+ /* bool is_ongoing = 22 */ r.getSbn().isOngoing(),
+ /* bool is_foreground_service = 23 */
+ NotificationRecordLogger.isForegroundService(r),
+ /* optional int64 timeout_millis = 24 */
+ r.getSbn().getNotification().getTimeoutAfter()
);
}
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 3369dcd..3d328f8 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -32,7 +32,7 @@
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.PackageParser.SigningDetails;
+import android.content.pm.SigningDetails;
import android.content.pm.parsing.PackageInfoWithoutStateUtils;
import android.content.pm.parsing.ParsingPackageUtils;
import android.os.Binder;
diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java
index 2851107..e0f1065 100644
--- a/services/core/java/com/android/server/pm/ApkChecksums.java
+++ b/services/core/java/com/android/server/pm/ApkChecksums.java
@@ -37,6 +37,7 @@
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageParser;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails.SignatureSchemeVersion;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.os.Handler;
import android.os.RemoteException;
@@ -461,8 +462,8 @@
}
// Obtaining array of certificates used for signing the installer package.
- certs = installer.getSigningDetails().signatures;
- pastCerts = installer.getSigningDetails().pastSigningCertificates;
+ certs = installer.getSigningDetails().getSignatures();
+ pastCerts = installer.getSigningDetails().getPastSigningCertificates();
}
if (certs == null || certs.length == 0 || certs[0] == null) {
Slog.e(TAG, "Can't obtain certificates.");
@@ -664,8 +665,7 @@
Map<Integer, byte[]> contentDigests = null;
try {
contentDigests = ApkSignatureVerifier.verifySignaturesInternal(filePath,
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2,
- false).contentDigests;
+ SignatureSchemeVersion.SIGNING_BLOCK_V2, /* verifyFull */ false).contentDigests;
} catch (PackageParser.PackageParserException e) {
if (!(e.getCause() instanceof SignatureNotFoundException)) {
Slog.e(TAG, "Signature verification error", e);
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 06ff691..d4af254 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -28,7 +28,7 @@
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
+import android.content.pm.SigningDetails;
import android.content.pm.UserInfo;
import android.content.pm.parsing.component.ParsedComponent;
import android.content.pm.parsing.component.ParsedInstrumentation;
@@ -147,7 +147,7 @@
private final OverlayReferenceMapper mOverlayReferenceMapper;
private final StateProvider mStateProvider;
- private PackageParser.SigningDetails mSystemSigningDetails;
+ private SigningDetails mSystemSigningDetails;
private Set<String> mProtectedBroadcasts = new ArraySet<>();
private final Object mCacheLock = new Object();
@@ -951,7 +951,7 @@
}
}
- private static boolean isSystemSigned(@NonNull PackageParser.SigningDetails sysSigningDetails,
+ private static boolean isSystemSigned(@NonNull SigningDetails sysSigningDetails,
PackageSetting pkgSetting) {
return pkgSetting.isSystem()
&& pkgSetting.signatures.mSigningDetails.signaturesMatchExactly(sysSigningDetails);
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index bf323e7..affacbb 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -23,8 +23,8 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.InstantAppInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
import android.content.pm.PermissionInfo;
+import android.content.pm.SigningDetails;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
@@ -412,14 +412,14 @@
// into account but also allow the value from the old computation to avoid
// data loss.
if (pkg.getSigningDetails().checkCapability(currentCookieSha256,
- PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)) {
+ SigningDetails.CertCapabilities.INSTALLED_DATA)) {
return;
}
// For backwards compatibility we accept match based on any signature, since we may have
// recorded only the first for multiply-signed packages
- final String[] signaturesSha256Digests =
- PackageUtils.computeSignaturesSha256Digests(pkg.getSigningDetails().signatures);
+ final String[] signaturesSha256Digests = PackageUtils.computeSignaturesSha256Digests(
+ pkg.getSigningDetails().getSignatures());
for (String s : signaturesSha256Digests) {
if (s.equals(currentCookieSha256)) {
return;
@@ -1305,8 +1305,8 @@
// We prefer the modern computation procedure where all certs are taken
// into account and delete the file derived via the legacy hash computation.
File newCookieFile = computeInstantCookieFile(pkg.getPackageName(),
- PackageUtils.computeSignaturesSha256Digest(pkg.getSigningDetails().signatures),
- userId);
+ PackageUtils.computeSignaturesSha256Digest(
+ pkg.getSigningDetails().getSignatures()), userId);
if (!pkg.getSigningDetails().hasSignatures()) {
Slog.wtf(LOG_TAG, "Parsed Instant App contains no valid signatures!");
}
diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java
index 34caaf5..fe74889 100644
--- a/services/core/java/com/android/server/pm/KeySetManagerService.java
+++ b/services/core/java/com/android/server/pm/KeySetManagerService.java
@@ -193,7 +193,7 @@
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Passed invalid package to keyset validation.");
}
- ArraySet<PublicKey> signingKeys = pkg.getSigningDetails().publicKeys;
+ ArraySet<PublicKey> signingKeys = pkg.getSigningDetails().getPublicKeys();
if (signingKeys == null || !(signingKeys.size() > 0) || signingKeys.contains(null)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Package has invalid signing-key-set.");
@@ -226,7 +226,7 @@
PackageSetting ps = mPackages.get(pkg.getPackageName());
Objects.requireNonNull(ps, "pkg: " + pkg.getPackageName()
+ "does not have a corresponding entry in mPackages.");
- addSigningKeySetToPackageLPw(ps, pkg.getSigningDetails().publicKeys);
+ addSigningKeySetToPackageLPw(ps, pkg.getSigningDetails().getPublicKeys());
if (pkg.getKeySetMapping() != null) {
addDefinedKeySetsToPackageLPw(ps, pkg.getKeySetMapping());
if (pkg.getUpgradeKeySets() != null) {
@@ -371,7 +371,7 @@
for (int i = 0; i < upgradeKeySets.length; i++) {
Set<PublicKey> upgradeSet = getPublicKeysFromKeySetLPr(upgradeKeySets[i]);
if (upgradeSet != null
- && pkg.getSigningDetails().publicKeys.containsAll(upgradeSet)) {
+ && pkg.getSigningDetails().getPublicKeys().containsAll(upgradeSet)) {
return true;
}
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 0b63b7d..e9d49f0 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -253,7 +253,8 @@
mSessionsDir.mkdirs();
mApexManager = ApexManager.getInstance();
- mStagingManager = new StagingManager(context, apexParserSupplier);
+ mStagingManager = new StagingManager(context, apexParserSupplier,
+ mInstallThread.getLooper());
LocalServices.getService(SystemServiceManager.class).startService(
new Lifecycle(context, this));
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 4b0eb65..af90115 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -46,9 +46,11 @@
import static com.android.server.pm.PackageInstallerService.prepareStageDir;
import android.Manifest;
+import android.annotation.AnyThread;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.WorkerThread;
import android.app.AppOpsManager;
import android.app.Notification;
import android.app.NotificationManager;
@@ -82,8 +84,8 @@
import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.SigningDetails;
import android.content.pm.dex.DexMetadataHelper;
import android.content.pm.parsing.ApkLite;
import android.content.pm.parsing.ApkLiteParseUtils;
@@ -262,6 +264,15 @@
private static final int INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS = 7000;
private static final int INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS = 60000;
+ /**
+ * The default value of {@link #mValidatedTargetSdk} is {@link Integer#MAX_VALUE}. If {@link
+ * #mValidatedTargetSdk} is compared with {@link Build.VERSION_CODES#Q} before getting the
+ * target sdk version from a validated apk in {@link #validateApkInstallLocked()}, the compared
+ * result will not trigger any user action in
+ * {@link #checkUserActionRequirement(PackageInstallerSession)}.
+ */
+ private static final int INVALID_TARGET_SDK_VERSION = Integer.MAX_VALUE;
+
// TODO: enforce INSTALL_ALLOW_TEST
// TODO: enforce INSTALL_ALLOW_DOWNGRADE
@@ -365,7 +376,7 @@
@GuardedBy("mLock")
private long mVersionCode;
@GuardedBy("mLock")
- private PackageParser.SigningDetails mSigningDetails;
+ private SigningDetails mSigningDetails;
@GuardedBy("mLock")
private SparseArray<PackageInstallerSession> mChildSessions = new SparseArray<>();
@GuardedBy("mLock")
@@ -704,28 +715,22 @@
}
/**
- * Notified by the staging manager that pre-reboot verification is about to start. The
- * return value should be checked to decide whether it is OK to start pre-reboot
- * verification. In the case of a destroyed session, {@code false} is returned and there is
- * no need to start pre-reboot verification.
+ * Called when pre-reboot verification is about to start. This shouldn't be called
+ * on a destroyed session.
*/
- @Override
- public boolean notifyStartPreRebootVerification() {
+ private void notifyStartPreRebootVerification() {
synchronized (mLock) {
+ Preconditions.checkState(!mDestroyed);
if (mInPreRebootVerification) {
throw new IllegalStateException("Pre-reboot verification has started");
}
- if (mDestroyed) {
- return false;
- }
mInPreRebootVerification = true;
- return true;
}
}
/**
- * Notified by the staging manager that pre-reboot verification has ended. Now it is safe to
- * clean up the session if {@link #abandon()} has been called previously.
+ * Notified by the staging manager or PIS that pre-reboot verification has ended.
+ * Now it is safe to clean up the session if {@link #abandon()} has been called previously.
*/
@Override
public void notifyEndPreRebootVerification() {
@@ -749,6 +754,7 @@
assertCallerIsOwnerOrRootOrSystem();
Preconditions.checkArgument(isCommitted());
Preconditions.checkArgument(!mSessionApplied && !mSessionFailed);
+ notifyStartPreRebootVerification();
verify();
}
@@ -802,6 +808,12 @@
@GuardedBy("mLock")
private PackageLite mPackageLite;
+ /**
+ * Keep the target sdk of a validated apk.
+ */
+ @GuardedBy("mLock")
+ private int mValidatedTargetSdk = INVALID_TARGET_SDK_VERSION;
+
private static final FileFilter sAddedApkFilter = new FileFilter() {
@Override
public boolean accept(File file) {
@@ -1734,6 +1746,7 @@
mHandler.obtainMessage(MSG_STREAM_VALIDATE_AND_COMMIT).sendToTarget();
}
+ @WorkerThread
private void handleStreamValidateAndCommit() {
PackageManagerException unrecoverableFailure = null;
// This will track whether the session and any children were validated and are ready to
@@ -1983,6 +1996,7 @@
* exception is thrown.
* @throws PackageManagerException on an unrecoverable error.
*/
+ @WorkerThread
private boolean streamValidateAndCommit() throws PackageManagerException {
// TODO(patb): since the work done here for a parent session in a multi-package install is
// mostly superficial, consider splitting this method for the parent and
@@ -2100,9 +2114,7 @@
if (isStaged()) {
mStagedSession.setSessionFailed(
SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, msgWithErrorCode);
- // TODO(b/136257624): Remove this once all verification logic has been transferred out
- // of StagingManager.
- mStagingManager.notifyVerificationComplete(mStagedSession);
+ mStagedSession.notifyEndPreRebootVerification();
} else {
// Dispatch message to remove session from PackageInstallerService.
dispatchSessionFinished(error, msg, null);
@@ -2146,6 +2158,7 @@
* immutable by the caller during the method call. Used to resolve child
* sessions Ids to actual object reference.
*/
+ @AnyThread
void onAfterSessionRead(SparseArray<PackageInstallerSession> allSessions) {
synchronized (mLock) {
// Resolve null values to actual object references
@@ -2231,6 +2244,63 @@
}
}
+ @WorkerThread
+ private static boolean checkUserActionRequirement(PackageInstallerSession session) {
+ if (session.isMultiPackage()) {
+ return false;
+ }
+
+ @UserActionRequirement int userActionRequirement = USER_ACTION_NOT_NEEDED;
+ // TODO(b/159331446): Move this to makeSessionActiveForInstall and update javadoc
+ userActionRequirement = session.computeUserActionRequirement();
+ if (userActionRequirement == USER_ACTION_REQUIRED) {
+ session.sendPendingUserActionIntent();
+ return true;
+ }
+
+ if (!session.isApexSession() && userActionRequirement == USER_ACTION_PENDING_APK_PARSING) {
+ final int validatedTargetSdk;
+ synchronized (session.mLock) {
+ validatedTargetSdk = session.mValidatedTargetSdk;
+ }
+
+ if (validatedTargetSdk != INVALID_TARGET_SDK_VERSION
+ && validatedTargetSdk < Build.VERSION_CODES.Q) {
+ session.sendPendingUserActionIntent();
+ return true;
+ }
+
+ if (session.params.requireUserAction == SessionParams.USER_ACTION_NOT_REQUIRED) {
+ if (!session.mSilentUpdatePolicy.isSilentUpdateAllowed(
+ session.getInstallerPackageName(), session.getPackageName())) {
+ // Fall back to the non-silent update if a repeated installation is invoked
+ // within the throttle time.
+ session.sendPendingUserActionIntent();
+ return true;
+ }
+ session.mSilentUpdatePolicy.track(session.getInstallerPackageName(),
+ session.getPackageName());
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Find out any session needs user action.
+ *
+ * @return true if the session set requires user action for the installation, otherwise false.
+ */
+ @WorkerThread
+ private boolean sendPendingUserActionIntentIfNeeded() {
+ synchronized (mLock) {
+ assertNotChildLocked("PackageInstallerSession#sendPendingUserActionIntentIfNeeded");
+ }
+
+ return sessionContains(PackageInstallerSession::checkUserActionRequirement);
+ }
+
+ @WorkerThread
private void handleInstall() {
if (isInstallerDeviceOwnerOrAffiliatedProfileOwner()) {
DevicePolicyEventLogger
@@ -2238,15 +2308,26 @@
.setAdmin(mInstallSource.installerPackageName)
.write();
}
- if (params.isStaged) {
- mStagingManager.commitSession(mStagedSession);
- // TODO(b/136257624): CTS test fails if we don't send session finished broadcast, even
- // though ideally, we just need to send session committed broadcast.
- dispatchSessionFinished(INSTALL_SUCCEEDED, "Session staged", null);
+
+ /**
+ * Stops the installation of the whole session set if one session needs user action
+ * in its belong session set. When the user answers the yes,
+ * {@link #setPermissionsResult(boolean)} is called and then {@link #MSG_INSTALL} is
+ * handled to come back here to check again.
+ */
+ if (sendPendingUserActionIntentIfNeeded()) {
return;
}
- verify();
+ if (params.isStaged) {
+ // TODO(b/136257624): CTS test fails if we don't send session finished broadcast, even
+ // though ideally, we just need to send session committed broadcast.
+ dispatchSessionFinished(INSTALL_SUCCEEDED, "Session staged", null);
+
+ mStagedSession.verifySession();
+ } else {
+ verify();
+ }
}
private void verify() {
@@ -2258,18 +2339,18 @@
}
}
+ private IntentSender getRemoteStatusReceiver() {
+ synchronized (mLock) {
+ return mRemoteStatusReceiver;
+ }
+ }
+
private void verifyNonStaged()
throws PackageManagerException {
final PackageManagerService.VerificationParams verifyingSession =
prepareForVerification();
- if (verifyingSession == null) {
- return;
- }
if (isMultiPackage()) {
- final List<PackageInstallerSession> childSessions;
- synchronized (mLock) {
- childSessions = getChildSessionsLocked();
- }
+ final List<PackageInstallerSession> childSessions = getChildSessions();
// Spot check to reject a non-staged multi package install of APEXes and APKs.
if (!params.isStaged && containsApkSession()
&& sessionContains(s -> s.isApexSession())) {
@@ -2286,20 +2367,14 @@
try {
final PackageManagerService.VerificationParams verifyingChildSession =
session.prepareForVerification();
- if (verifyingChildSession != null) {
- verifyingChildSessions.add(verifyingChildSession);
- }
+ verifyingChildSessions.add(verifyingChildSession);
} catch (PackageManagerException e) {
failure = e;
success = false;
}
}
if (!success) {
- final IntentSender statusReceiver;
- synchronized (mLock) {
- statusReceiver = mRemoteStatusReceiver;
- }
- sendOnPackageInstalled(mContext, statusReceiver, sessionId,
+ sendOnPackageInstalled(mContext, getRemoteStatusReceiver(), sessionId,
isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId, null,
failure.error, failure.getLocalizedMessage(), null);
return;
@@ -2327,10 +2402,7 @@
"Session should contain at least one apk session for installation");
}
if (isMultiPackage()) {
- final List<PackageInstallerSession> childSessions;
- synchronized (mLock) {
- childSessions = getChildSessionsLocked();
- }
+ final List<PackageInstallerSession> childSessions = getChildSessions();
List<PackageManagerService.InstallParams> installingChildSessions =
new ArrayList<>(childSessions.size());
boolean success = true;
@@ -2349,11 +2421,7 @@
}
}
if (!success) {
- final IntentSender statusReceiver;
- synchronized (mLock) {
- statusReceiver = mRemoteStatusReceiver;
- }
- sendOnPackageInstalled(mContext, statusReceiver, sessionId,
+ sendOnPackageInstalled(mContext, getRemoteStatusReceiver(), sessionId,
isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId, null,
failure.error, failure.getLocalizedMessage(), null);
return;
@@ -2369,23 +2437,11 @@
* {@link PackageManagerService.VerificationParams} representing this new staged state or null
* in case permissions need to be requested before verification can proceed.
*/
- @Nullable
+ @NonNull
private PackageManagerService.VerificationParams prepareForVerification()
throws PackageManagerException {
assertNotLocked("makeSessionActive");
- @UserActionRequirement
- int userActionRequirement = USER_ACTION_NOT_NEEDED;
- // TODO(b/159331446): Move this to makeSessionActiveForInstall and update javadoc
- if (!params.isMultiPackage) {
- userActionRequirement = computeUserActionRequirement();
- if (userActionRequirement == USER_ACTION_REQUIRED) {
- sendPendingUserActionIntent();
- return null;
- } // else, we'll wait until we parse to determine if we need to
- }
-
- boolean silentUpdatePolicyEnforceable = false;
synchronized (mLock) {
if (mRelinquished) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
@@ -2402,36 +2458,16 @@
PackageLite result = parseApkLite();
if (result != null) {
mPackageLite = result;
- synchronized (mProgressLock) {
- mInternalProgress = 0.5f;
- computeProgressLocked(true);
- }
-
- extractNativeLibraries(
- mPackageLite, stageDir, params.abiOverride, mayInheritNativeLibs());
-
- if (userActionRequirement == USER_ACTION_PENDING_APK_PARSING) {
- if (result.getTargetSdk() < Build.VERSION_CODES.Q) {
- sendPendingUserActionIntent();
- return null;
+ if (!isApexSession()) {
+ synchronized (mProgressLock) {
+ mInternalProgress = 0.5f;
+ computeProgressLocked(true);
}
- if (params.requireUserAction == SessionParams.USER_ACTION_NOT_REQUIRED) {
- silentUpdatePolicyEnforceable = true;
- }
+
+ extractNativeLibraries(
+ mPackageLite, stageDir, params.abiOverride, mayInheritNativeLibs());
}
}
- }
- if (silentUpdatePolicyEnforceable) {
- if (!mSilentUpdatePolicy.isSilentUpdateAllowed(
- getInstallerPackageName(), getPackageName())) {
- // Fall back to the non-silent update if a repeated installation is invoked within
- // the throttle time.
- sendPendingUserActionIntent();
- return null;
- }
- mSilentUpdatePolicy.track(getInstallerPackageName(), getPackageName());
- }
- synchronized (mLock) {
return makeVerificationParamsLocked();
}
}
@@ -2444,11 +2480,7 @@
intent.setPackage(mPm.getPackageInstallerPackageName());
intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
- final IntentSender statusReceiver;
- synchronized (mLock) {
- statusReceiver = mRemoteStatusReceiver;
- }
- sendOnUserActionRequired(mContext, statusReceiver, sessionId, intent);
+ sendOnUserActionRequired(mContext, getRemoteStatusReceiver(), sessionId, intent);
// Commit was keeping session marked as active until now; release
// that extra refcount so session appears idle.
@@ -2458,73 +2490,77 @@
/**
* Prepares staged directory with any inherited APKs and returns the parsed package.
*/
+ @GuardedBy("mLock")
@Nullable
private PackageLite parseApkLite() throws PackageManagerException {
- // TODO(b/136257624): Some logic in this if block probably belongs in
- // makeInstallParams().
- if (!isMultiPackage() && !isApexSession()) {
- Objects.requireNonNull(mPackageName);
- Objects.requireNonNull(mSigningDetails);
- Objects.requireNonNull(mResolvedBaseFile);
+ if (isMultiPackage()) {
+ return null;
+ }
+ Objects.requireNonNull(mPackageName);
+ Objects.requireNonNull(mSigningDetails);
+ Objects.requireNonNull(mResolvedBaseFile);
- // If we haven't already parsed, inherit any packages and native libraries from existing
- // install that haven't been overridden.
- if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
- try {
- final List<File> fromFiles = mResolvedInheritedFiles;
- final File toDir = stageDir;
+ // Inherit any packages and native libraries from existing install that
+ // haven't been overridden.
+ if (!isApexSession() && params.mode == SessionParams.MODE_INHERIT_EXISTING) {
+ try {
+ final List<File> fromFiles = mResolvedInheritedFiles;
+ final File toDir = stageDir;
- if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles);
- if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {
- throw new IllegalStateException("mInheritedFilesBase == null");
- }
-
- if (isLinkPossible(fromFiles, toDir)) {
- if (!mResolvedInstructionSets.isEmpty()) {
- final File oatDir = new File(toDir, "oat");
- createOatDirs(mResolvedInstructionSets, oatDir);
- }
- // pre-create lib dirs for linking if necessary
- if (!mResolvedNativeLibPaths.isEmpty()) {
- for (String libPath : mResolvedNativeLibPaths) {
- // "/lib/arm64" -> ["lib", "arm64"]
- final int splitIndex = libPath.lastIndexOf('/');
- if (splitIndex < 0 || splitIndex >= libPath.length() - 1) {
- Slog.e(TAG,
- "Skipping native library creation for linking due"
- + " to invalid path: " + libPath);
- continue;
- }
- final String libDirPath = libPath.substring(1, splitIndex);
- final File libDir = new File(toDir, libDirPath);
- if (!libDir.exists()) {
- NativeLibraryHelper.createNativeLibrarySubdir(libDir);
- }
- final String archDirPath = libPath.substring(splitIndex + 1);
- NativeLibraryHelper.createNativeLibrarySubdir(
- new File(libDir, archDirPath));
- }
- }
- linkFiles(fromFiles, toDir, mInheritedFilesBase);
- } else {
- // TODO: this should delegate to DCS so the system process
- // avoids holding open FDs into containers.
- copyFiles(fromFiles, toDir);
- }
- } catch (IOException e) {
- throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
- "Failed to inherit existing install", e);
+ if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles);
+ if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {
+ throw new IllegalStateException("mInheritedFilesBase == null");
}
+
+ if (isLinkPossible(fromFiles, toDir)) {
+ if (!mResolvedInstructionSets.isEmpty()) {
+ final File oatDir = new File(toDir, "oat");
+ createOatDirs(mResolvedInstructionSets, oatDir);
+ }
+ // pre-create lib dirs for linking if necessary
+ if (!mResolvedNativeLibPaths.isEmpty()) {
+ for (String libPath : mResolvedNativeLibPaths) {
+ // "/lib/arm64" -> ["lib", "arm64"]
+ final int splitIndex = libPath.lastIndexOf('/');
+ if (splitIndex < 0 || splitIndex >= libPath.length() - 1) {
+ Slog.e(TAG,
+ "Skipping native library creation for linking due"
+ + " to invalid path: " + libPath);
+ continue;
+ }
+ final String libDirPath = libPath.substring(1, splitIndex);
+ final File libDir = new File(toDir, libDirPath);
+ if (!libDir.exists()) {
+ NativeLibraryHelper.createNativeLibrarySubdir(libDir);
+ }
+ final String archDirPath = libPath.substring(splitIndex + 1);
+ NativeLibraryHelper.createNativeLibrarySubdir(
+ new File(libDir, archDirPath));
+ }
+ }
+ linkFiles(fromFiles, toDir, mInheritedFilesBase);
+ } else {
+ // TODO: this should delegate to DCS so the system process
+ // avoids holding open FDs into containers.
+ copyFiles(fromFiles, toDir);
+ }
+ } catch (IOException e) {
+ throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
+ "Failed to inherit existing install", e);
}
+ }
+
+ if (!isApexSession()) {
// For mode inherit existing, it would link/copy existing files to stage dir in the
// above block. Therefore, we need to parse the complete package in stage dir here.
- // Besides, PackageLite may be null for staged sessions that don't complete pre-reboot
- // verification.
+ // Besides, PackageLite may be null for staged sessions that don't complete
+ // pre-reboot verification.
return getOrParsePackageLiteLocked(stageDir, /* flags */ 0);
+ } else {
+ return getOrParsePackageLiteLocked(mResolvedBaseFile, /* flags */ 0);
}
- return null;
}
@GuardedBy("mLock")
@@ -2565,25 +2601,16 @@
mRelinquished = true;
- // TODO(b/169375643): Remove this workaround once b/161121612 is fixed.
- PackageInstaller.SessionParams copiedParams = params.copy();
- if (params.isStaged) {
- // This is called by the pre-reboot verification. Don't enable rollback here since
- // it has been enabled when pre-reboot verification starts.
- copiedParams.installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK;
- }
- return mPm.new VerificationParams(user, stageDir, localObserver, copiedParams,
+ return mPm.new VerificationParams(user, stageDir, localObserver, params,
mInstallSource, mInstallerUid, mSigningDetails, sessionId, mPackageLite);
}
private void onVerificationComplete() {
- // Staged sessions will be installed later during boot
+ // APK verification is done. Continue the installation depending on whether it is a
+ // staged session or not. For a staged session, we will hand it over to the staging
+ // manager to complete the installation.
if (isStaged()) {
- // TODO(b/136257624): Remove this once all verification logic has been transferred out
- // of StagingManager.
- mStagingManager.notifyPreRebootVerification_Apk_Complete(mStagedSession);
- // TODO(b/136257624): We also need to destroy internals for verified staged session,
- // otherwise file descriptors are never closed for verified staged session until reboot
+ mStagingManager.commitSession(mStagedSession);
return;
}
@@ -2828,6 +2855,8 @@
mPackageName = apk.getPackageName();
mVersionCode = apk.getLongVersionCode();
}
+
+ mSigningDetails = apk.getSigningDetails();
}
/**
@@ -2846,11 +2875,11 @@
@GuardedBy("mLock")
private PackageLite validateApkInstallLocked() throws PackageManagerException {
ApkLite baseApk = null;
- PackageLite packageLite = null;
+ final PackageLite packageLite;
mPackageLite = null;
mPackageName = null;
mVersionCode = -1;
- mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
+ mSigningDetails = SigningDetails.UNKNOWN;
mResolvedBaseFile = null;
mResolvedStagedFiles.clear();
@@ -2913,7 +2942,7 @@
mPackageName = apk.getPackageName();
mVersionCode = apk.getLongVersionCode();
}
- if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) {
+ if (mSigningDetails == SigningDetails.UNKNOWN) {
mSigningDetails = apk.getSigningDetails();
}
@@ -2968,7 +2997,7 @@
mPackageName = pkgInfo.packageName;
mVersionCode = pkgInfo.getLongVersionCode();
}
- if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) {
+ if (mSigningDetails == SigningDetails.UNKNOWN) {
mSigningDetails = unsafeGetCertsWithoutVerification(
pkgInfo.applicationInfo.sourceDir);
}
@@ -3028,7 +3057,7 @@
packageLite = existing;
assertPackageConsistentLocked("Existing", existing.getPackageName(),
existing.getLongVersionCode());
- final PackageParser.SigningDetails signingDetails =
+ final SigningDetails signingDetails =
unsafeGetCertsWithoutVerification(existing.getBaseApkPath());
if (!mSigningDetails.signaturesMatchExactly(signingDetails)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
@@ -3153,6 +3182,11 @@
mIncrementalFileStorages.disallowReadLogs();
}
}
+
+ // {@link #sendPendingUserActionIntentIfNeeded} needs to use
+ // {@link PackageLite#getTargetSdk()}
+ mValidatedTargetSdk = packageLite.getTargetSdk();
+
return packageLite;
}
@@ -3368,11 +3402,11 @@
}
}
- private PackageParser.SigningDetails unsafeGetCertsWithoutVerification(String path)
+ private SigningDetails unsafeGetCertsWithoutVerification(String path)
throws PackageManagerException {
try {
return ApkSignatureVerifier.unsafeGetCertsWithoutVerification(path,
- PackageParser.SigningDetails.SignatureSchemeVersion.JAR);
+ SigningDetails.SignatureSchemeVersion.JAR);
} catch (PackageParserException e) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Couldn't obtain signatures from APK : " + path);
@@ -3564,8 +3598,13 @@
// Mark and kick off another install pass
synchronized (mLock) {
mPermissionsManuallyAccepted = true;
- mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
}
+
+ PackageInstallerSession root =
+ (hasParentSessionId())
+ ? mSessionProvider.getSession(getParentSessionId())
+ : this;
+ root.mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
} else {
destroyInternal();
dispatchSessionFinished(INSTALL_FAILED_ABORTED, "User rejected permissions", null);
@@ -3851,11 +3890,7 @@
}
case IDataLoaderStatusListener.DATA_LOADER_UNAVAILABLE: {
// Don't fail or commit the session. Allow caller to commit again.
- final IntentSender statusReceiver;
- synchronized (mLock) {
- statusReceiver = mRemoteStatusReceiver;
- }
- sendPendingStreaming(mContext, statusReceiver, sessionId,
+ sendPendingStreaming(mContext, getRemoteStatusReceiver(), sessionId,
"DataLoader unavailable");
break;
}
@@ -3869,11 +3904,8 @@
} catch (RemoteException e) {
// In case of streaming failure we don't want to fail or commit the session.
// Just return from this method and allow caller to commit again.
- final IntentSender statusReceiver;
- synchronized (mLock) {
- statusReceiver = mRemoteStatusReceiver;
- }
- sendPendingStreaming(mContext, statusReceiver, sessionId, e.getMessage());
+ sendPendingStreaming(mContext, getRemoteStatusReceiver(), sessionId,
+ e.getMessage());
}
}
};
@@ -4142,18 +4174,13 @@
}
private void sendUpdateToRemoteStatusReceiver(int returnCode, String msg, Bundle extras) {
- final IntentSender statusReceiver;
- final String packageName;
- synchronized (mLock) {
- statusReceiver = mRemoteStatusReceiver;
- packageName = mPackageName;
- }
+ final IntentSender statusReceiver = getRemoteStatusReceiver();
if (statusReceiver != null) {
// Execute observer.onPackageInstalled on different thread as we don't want callers
// inside the system server have to worry about catching the callbacks while they are
// calling into the session
final SomeArgs args = SomeArgs.obtain();
- args.arg1 = packageName;
+ args.arg1 = getPackageName();
args.arg2 = msg;
args.arg3 = extras;
args.arg4 = statusReceiver;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index a8cc5fd..61c3e1d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -95,7 +95,7 @@
import static android.content.pm.PackageManager.TYPE_UNKNOWN;
import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
import static android.content.pm.PackageManagerInternal.LAST_KNOWN_PACKAGE;
-import static android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4;
+import static android.content.pm.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4;
import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile;
import static android.os.PowerWhitelistManager.REASON_LOCKED_BOOT_COMPLETED;
import static android.os.PowerWhitelistManager.REASON_PACKAGE_REPLACED;
@@ -149,6 +149,7 @@
import android.app.ApplicationPackageManager;
import android.app.BroadcastOptions;
import android.app.IActivityManager;
+import android.app.PendingIntent;
import android.app.ResourcesManager;
import android.app.admin.IDevicePolicyManager;
import android.app.admin.SecurityLog;
@@ -161,6 +162,7 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.IIntentReceiver;
+import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
@@ -212,8 +214,6 @@
import android.content.pm.PackageManagerInternal.PrivateResolveFlags;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.PackageParser.SigningDetails;
-import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
import android.content.pm.PackagePartitions;
import android.content.pm.PackagePartitions.SystemPartition;
import android.content.pm.PackageStats;
@@ -228,6 +228,8 @@
import android.content.pm.ServiceInfo;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
+import android.content.pm.SigningDetails.SignatureSchemeVersion;
import android.content.pm.SigningInfo;
import android.content.pm.SuspendDialogInfo;
import android.content.pm.TestUtilityService;
@@ -1340,7 +1342,6 @@
public DefaultAppProvider defaultAppProvider;
public DexManager dexManager;
public List<ScanPartition> dirsToScanAsSystem;
- public @Nullable String documenterPackage;
public boolean factoryTest;
public ArrayMap<String, FeatureInfo> availableFeatures;
public Handler handler;
@@ -1689,7 +1690,6 @@
final @Nullable String mStorageManagerPackage;
final @Nullable String mDefaultTextClassifierPackage;
final @Nullable String mSystemTextClassifierPackageName;
- final @Nullable String mDocumenterPackage;
final @Nullable String mConfiguratorPackage;
final @Nullable String mAppPredictionServicePackage;
final @Nullable String mIncidentReportApproverPackage;
@@ -6876,7 +6876,6 @@
mSystemTextClassifierPackageName = testParams.systemTextClassifierPackage;
mRetailDemoPackage = testParams.retailDemoPackage;
mRecentsPackage = testParams.recentsPackage;
- mDocumenterPackage = testParams.documenterPackage;
mConfiguratorPackage = testParams.configuratorPackage;
mAppPredictionServicePackage = testParams.appPredictionServicePackage;
mIncidentReportApproverPackage = testParams.incidentReportApproverPackage;
@@ -7475,7 +7474,6 @@
mDefaultTextClassifierPackage = getDefaultTextClassifierPackageName();
mSystemTextClassifierPackageName = getSystemTextClassifierPackageName();
- mDocumenterPackage = getDocumenterPackageName();
mConfiguratorPackage = getDeviceConfiguratorPackageName();
mAppPredictionServicePackage = getAppPredictionServicePackageName();
mIncidentReportApproverPackage = getIncidentReportApproverPackageName();
@@ -9656,7 +9654,8 @@
if (p2SigningDetails == null) {
return PackageManager.SIGNATURE_SECOND_NOT_SIGNED;
}
- int result = compareSignatures(p1SigningDetails.signatures, p2SigningDetails.signatures);
+ int result = compareSignatures(p1SigningDetails.getSignatures(),
+ p2SigningDetails.getSignatures());
if (result == PackageManager.SIGNATURE_MATCH) {
return result;
}
@@ -9666,11 +9665,11 @@
if (p1SigningDetails.hasPastSigningCertificates()
|| p2SigningDetails.hasPastSigningCertificates()) {
Signature[] p1Signatures = p1SigningDetails.hasPastSigningCertificates()
- ? new Signature[]{p1SigningDetails.pastSigningCertificates[0]}
- : p1SigningDetails.signatures;
+ ? new Signature[]{p1SigningDetails.getPastSigningCertificates()[0]}
+ : p1SigningDetails.getSignatures();
Signature[] p2Signatures = p2SigningDetails.hasPastSigningCertificates()
- ? new Signature[]{p2SigningDetails.pastSigningCertificates[0]}
- : p2SigningDetails.signatures;
+ ? new Signature[]{p2SigningDetails.getPastSigningCertificates()[0]}
+ : p2SigningDetails.getSignatures();
result = compareSignatures(p1Signatures, p2Signatures);
}
return result;
@@ -9714,7 +9713,7 @@
final int appId = UserHandle.getAppId(uid);
// reader
synchronized (mLock) {
- final PackageParser.SigningDetails signingDetails;
+ final SigningDetails signingDetails;
final Object obj = mSettings.getSettingLPr(appId);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
@@ -11729,14 +11728,14 @@
&& ps.timeStamp == lastModifiedTime
&& !isCompatSignatureUpdateNeeded(settingsVersionForPackage)
&& !isRecoverSignatureUpdateNeeded(settingsVersionForPackage)) {
- if (ps.signatures.mSigningDetails.signatures != null
- && ps.signatures.mSigningDetails.signatures.length != 0
- && ps.signatures.mSigningDetails.signatureSchemeVersion
+ if (ps.signatures.mSigningDetails.getSignatures() != null
+ && ps.signatures.mSigningDetails.getSignatures().length != 0
+ && ps.signatures.mSigningDetails.getSignatureSchemeVersion()
!= SignatureSchemeVersion.UNKNOWN) {
// Optimization: reuse the existing cached signing data
// if the package appears to be unchanged.
parsedPackage.setSigningDetails(
- new PackageParser.SigningDetails(ps.signatures.mSigningDetails));
+ new SigningDetails(ps.signatures.mSigningDetails));
return;
}
@@ -12040,10 +12039,10 @@
if (!parsedPackage.getSigningDetails()
.checkCapability(pkgSetting.signatures.mSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
+ SigningDetails.CertCapabilities.INSTALLED_DATA)
&& !pkgSetting.signatures.mSigningDetails.checkCapability(
parsedPackage.getSigningDetails(),
- PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) {
+ SigningDetails.CertCapabilities.ROLLBACK)) {
logCriticalInfo(Log.WARN,
"System package signature mismatch;"
+ " name: " + pkgSetting.name);
@@ -13357,9 +13356,9 @@
// For apps targeting O MR1 we require explicit enumeration of all certs.
final String[] libCertDigests = (targetSdk >= Build.VERSION_CODES.O_MR1)
? PackageUtils.computeSignaturesSha256Digests(
- libPkg.signatures)
+ libPkg.getSignatures())
: PackageUtils.computeSignaturesSha256Digests(
- new Signature[]{libPkg.signatures[0]});
+ new Signature[]{libPkg.getSignatures()[0]});
// Take a shortcut if sizes don't match. Note that if an app doesn't
// target O we don't parse the "additional-certificate" tags similarly
@@ -13700,8 +13699,9 @@
// priv-apps.
synchronized (mLock) {
PackageSetting platformPkgSetting = mSettings.getPackageLPr("android");
- if ((compareSignatures(platformPkgSetting.signatures.mSigningDetails.signatures,
- pkg.getSigningDetails().signatures)
+ if ((compareSignatures(
+ platformPkgSetting.signatures.mSigningDetails.getSignatures(),
+ pkg.getSigningDetails().getSignatures())
!= PackageManager.SIGNATURE_MATCH)) {
scanFlags |= SCAN_AS_PRIVILEGED;
}
@@ -14540,8 +14540,8 @@
parsedPackage.setSignedWithPlatformKey(
(PLATFORM_PACKAGE_NAME.equals(parsedPackage.getPackageName())
|| (platformPkg != null && compareSignatures(
- platformPkg.getSigningDetails().signatures,
- parsedPackage.getSigningDetails().signatures
+ platformPkg.getSigningDetails().getSignatures(),
+ parsedPackage.getSigningDetails().getSignatures()
) == PackageManager.SIGNATURE_MATCH))
);
@@ -14837,7 +14837,7 @@
// Exempt SharedUsers signed with the platform key.
PackageSetting platformPkgSetting = mSettings.getPackageLPr("android");
if (!comparePackageSignatures(platformPkgSetting,
- pkg.getSigningDetails().signatures)) {
+ pkg.getSigningDetails().getSignatures())) {
throw new PackageManagerException("Apps that share a user with a " +
"privileged app must themselves be marked as privileged. " +
pkg.getPackageName() + " shares privileged user " +
@@ -14885,7 +14885,7 @@
final PackageSetting platformPkgSetting =
mSettings.getPackageLPr("android");
if (!comparePackageSignatures(platformPkgSetting,
- pkg.getSigningDetails().signatures)) {
+ pkg.getSigningDetails().getSignatures())) {
throw new PackageManagerException("Overlay "
+ pkg.getPackageName()
+ " must target Q or later, "
@@ -14905,7 +14905,7 @@
mSettings.getPackageLPr(pkg.getOverlayTarget());
if (targetPkgSetting != null) {
if (!comparePackageSignatures(targetPkgSetting,
- pkg.getSigningDetails().signatures)) {
+ pkg.getSigningDetails().getSignatures())) {
// check reference signature
if (mOverlayConfigSignaturePackage == null) {
throw new PackageManagerException("Overlay "
@@ -14917,7 +14917,7 @@
final PackageSetting refPkgSetting =
mSettings.getPackageLPr(mOverlayConfigSignaturePackage);
if (!comparePackageSignatures(refPkgSetting,
- pkg.getSigningDetails().signatures)) {
+ pkg.getSigningDetails().getSignatures())) {
throw new PackageManagerException("Overlay "
+ pkg.getPackageName() + " signed with a different "
+ "certificate than both the reference package and "
@@ -14936,7 +14936,8 @@
int minSignatureSchemeVersion =
ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
pkg.getTargetSdkVersion());
- if (pkg.getSigningDetails().signatureSchemeVersion < minSignatureSchemeVersion) {
+ if (pkg.getSigningDetails().getSignatureSchemeVersion()
+ < minSignatureSchemeVersion) {
throw new PackageManagerException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No signature found in package of version " + minSignatureSchemeVersion
+ " or newer for package " + pkg.getPackageName());
@@ -16746,7 +16747,7 @@
final AndroidPackage pkg = mPackages.get(verifierInfo.packageName);
if (pkg == null) {
return -1;
- } else if (pkg.getSigningDetails().signatures.length != 1) {
+ } else if (pkg.getSigningDetails().getSignatures().length != 1) {
Slog.i(TAG, "Verifier package " + verifierInfo.packageName
+ " has more than one signature; ignoring");
return -1;
@@ -16760,7 +16761,7 @@
final byte[] expectedPublicKey;
try {
- final Signature verifierSig = pkg.getSigningDetails().signatures[0];
+ final Signature verifierSig = pkg.getSigningDetails().getSignatures()[0];
final PublicKey publicKey = verifierSig.getPublicKey();
expectedPublicKey = publicKey.getEncoded();
} catch (CertificateException e) {
@@ -17018,9 +17019,10 @@
if (obj != null) {
if (obj instanceof SharedUserSetting) {
callerSignature =
- ((SharedUserSetting)obj).signatures.mSigningDetails.signatures;
+ ((SharedUserSetting) obj).signatures.mSigningDetails.getSignatures();
} else if (obj instanceof PackageSetting) {
- callerSignature = ((PackageSetting)obj).signatures.mSigningDetails.signatures;
+ callerSignature =
+ ((PackageSetting) obj).signatures.mSigningDetails.getSignatures();
} else {
throw new SecurityException("Bad object " + obj + " for uid " + callingUid);
}
@@ -17032,7 +17034,7 @@
// not signed with the same cert as the caller.
if (installerPackageSetting != null) {
if (compareSignatures(callerSignature,
- installerPackageSetting.signatures.mSigningDetails.signatures)
+ installerPackageSetting.signatures.mSigningDetails.getSignatures())
!= PackageManager.SIGNATURE_MATCH) {
throw new SecurityException(
"Caller does not have same cert as new installer package "
@@ -17049,7 +17051,7 @@
if (targetInstallerPkgSetting != null) {
if (compareSignatures(callerSignature,
- targetInstallerPkgSetting.signatures.mSigningDetails.signatures)
+ targetInstallerPkgSetting.signatures.mSigningDetails.getSignatures())
!= PackageManager.SIGNATURE_MATCH) {
throw new SecurityException(
"Caller does not have same cert as old installer package "
@@ -17620,7 +17622,7 @@
final String[] grantedRuntimePermissions;
final List<String> whitelistedRestrictedPermissions;
final int autoRevokePermissionsMode;
- final PackageParser.SigningDetails signingDetails;
+ final SigningDetails signingDetails;
final int installReason;
final int mInstallScenario;
@Nullable MultiPackageInstallParams mParentInstallParams;
@@ -17644,7 +17646,7 @@
this.grantedRuntimePermissions = null;
this.whitelistedRestrictedPermissions = null;
this.autoRevokePermissionsMode = MODE_DEFAULT;
- this.signingDetails = PackageParser.SigningDetails.UNKNOWN;
+ this.signingDetails = SigningDetails.UNKNOWN;
this.installReason = PackageManager.INSTALL_REASON_UNKNOWN;
this.mInstallScenario = PackageManager.INSTALL_SCENARIO_DEFAULT;
this.forceQueryableOverride = false;
@@ -17936,7 +17938,7 @@
@NonNull final InstallSource installSource;
final String packageAbiOverride;
final VerificationInfo verificationInfo;
- final PackageParser.SigningDetails signingDetails;
+ final SigningDetails signingDetails;
@Nullable MultiPackageVerificationParams mParentVerificationParams;
final long requiredInstalledVersionCode;
final int mDataLoaderType;
@@ -17979,13 +17981,6 @@
}
public void handleStartCopy() {
- if ((installFlags & PackageManager.INSTALL_APEX) != 0) {
- // Apex packages get verified in StagingManager currently.
- // TODO(b/136257624): Move apex verification logic out of StagingManager
- mRet = INSTALL_SUCCEEDED;
- return;
- }
-
PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
mPackageLite, origin.resolvedPath, installFlags, packageAbiOverride);
@@ -17997,7 +17992,10 @@
// Perform package verification and enable rollback (unless we are simply moving the
// package).
if (!origin.existing) {
- sendApkVerificationRequest(pkgLite);
+ if ((installFlags & PackageManager.INSTALL_APEX) == 0) {
+ // TODO(b/182426975): treat APEX as APK when APK verification is concerned
+ sendApkVerificationRequest(pkgLite);
+ }
if ((installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
sendEnableRollbackRequest();
}
@@ -18044,28 +18042,23 @@
// it will not miss the broadcast.
enableRollbackIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendOrderedBroadcastAsUser(enableRollbackIntent, UserHandle.SYSTEM,
- android.Manifest.permission.PACKAGE_ROLLBACK_AGENT,
- new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- // the duration to wait for rollback to be enabled, in millis
- long rollbackTimeout = DeviceConfig.getLong(
- DeviceConfig.NAMESPACE_ROLLBACK,
- PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS,
- DEFAULT_ENABLE_ROLLBACK_TIMEOUT_MILLIS);
- if (rollbackTimeout < 0) {
- rollbackTimeout = DEFAULT_ENABLE_ROLLBACK_TIMEOUT_MILLIS;
- }
- final Message msg = mHandler.obtainMessage(
- ENABLE_ROLLBACK_TIMEOUT);
- msg.arg1 = enableRollbackToken;
- msg.arg2 = mSessionId;
- mHandler.sendMessageDelayed(msg, rollbackTimeout);
- }
- }, null, 0, null, null);
+ mContext.sendBroadcastAsUser(enableRollbackIntent, UserHandle.SYSTEM,
+ android.Manifest.permission.PACKAGE_ROLLBACK_AGENT);
mWaitForEnableRollbackToComplete = true;
+
+ // the duration to wait for rollback to be enabled, in millis
+ long rollbackTimeout = DeviceConfig.getLong(
+ DeviceConfig.NAMESPACE_ROLLBACK,
+ PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS,
+ DEFAULT_ENABLE_ROLLBACK_TIMEOUT_MILLIS);
+ if (rollbackTimeout < 0) {
+ rollbackTimeout = DEFAULT_ENABLE_ROLLBACK_TIMEOUT_MILLIS;
+ }
+ final Message msg = mHandler.obtainMessage(ENABLE_ROLLBACK_TIMEOUT);
+ msg.arg1 = enableRollbackToken;
+ msg.arg2 = mSessionId;
+ mHandler.sendMessageDelayed(msg, rollbackTimeout);
}
/**
@@ -18158,7 +18151,7 @@
final boolean isVerificationEnabled = isVerificationEnabled(
pkgLite, verifierUser.getIdentifier(), installFlags, installerUid);
final boolean isV4Signed =
- (signingDetails.signatureSchemeVersion == SIGNING_BLOCK_V4);
+ (signingDetails.getSignatureSchemeVersion() == SIGNING_BLOCK_V4);
final boolean isIncrementalInstall =
(mDataLoaderType == DataLoaderType.INCREMENTAL);
// NOTE: We purposefully skip verification for only incremental installs when there's
@@ -18389,7 +18382,7 @@
/** If non-null, drop an async trace when the install completes */
final String traceMethod;
final int traceCookie;
- final PackageParser.SigningDetails signingDetails;
+ final SigningDetails signingDetails;
final int installReason;
final int mInstallScenario;
final boolean forceQueryableOverride;
@@ -18527,7 +18520,7 @@
FileInstallArgs(String codePath, String[] instructionSets) {
super(OriginInfo.fromNothing(), null, null, 0, InstallSource.EMPTY,
null, null, instructionSets, null, null, null, MODE_DEFAULT, null, 0,
- PackageParser.SigningDetails.UNKNOWN,
+ SigningDetails.UNKNOWN,
PackageManager.INSTALL_REASON_UNKNOWN, PackageManager.INSTALL_SCENARIO_DEFAULT,
false, DataLoaderType.NONE);
this.codeFile = (codePath != null) ? new File(codePath) : null;
@@ -19383,11 +19376,11 @@
// the signatures on the first package scanned for the shared user (i.e. if the
// signaturesChanged state hasn't been initialized yet in SharedUserSetting).
if (signatureCheckPs.sharedUser != null) {
- final Signature[] sharedUserSignatures =
- signatureCheckPs.sharedUser.signatures.mSigningDetails.signatures;
+ final Signature[] sharedUserSignatures = signatureCheckPs.sharedUser
+ .signatures.mSigningDetails.getSignatures();
if (signatureCheckPs.sharedUser.signaturesChanged != null
&& compareSignatures(sharedUserSignatures,
- parsedPackage.getSigningDetails().signatures)
+ parsedPackage.getSigningDetails().getSignatures())
!= PackageManager.SIGNATURE_MATCH) {
if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 29) {
// Mismatched signatures is an error and silently skipping system
@@ -19791,7 +19784,7 @@
if (args.mDataLoaderType != DataLoaderType.INCREMENTAL) {
continue;
}
- if (args.signingDetails.signatureSchemeVersion != SIGNING_BLOCK_V4) {
+ if (args.signingDetails.getSignatureSchemeVersion() != SIGNING_BLOCK_V4) {
continue;
}
// For incremental installs, we bypass the verifier prior to install. Now
@@ -20196,11 +20189,11 @@
// older certificate with which the current is ok with sharing permissions
if (sourceSigningDetails.checkCapability(
parsedPackage.getSigningDetails(),
- PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
+ SigningDetails.CertCapabilities.PERMISSION)) {
return true;
} else if (parsedPackage.getSigningDetails().checkCapability(
sourceSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
+ SigningDetails.CertCapabilities.PERMISSION)) {
// the scanned package checks out, has signing certificate rotation
// history, and is newer; bring it over
synchronized (mLock) {
@@ -20315,7 +20308,7 @@
try {
// either use what we've been given or parse directly from the APK
- if (args.signingDetails != PackageParser.SigningDetails.UNKNOWN) {
+ if (args.signingDetails != SigningDetails.UNKNOWN) {
parsedPackage.setSigningDetails(args.signingDetails);
} else {
parsedPackage.setSigningDetails(ParsingPackageUtils.getSigningDetails(
@@ -20325,7 +20318,7 @@
throw new PrepareFailure("Failed collect during installPackageLI", e);
}
- if (instantApp && parsedPackage.getSigningDetails().signatureSchemeVersion
+ if (instantApp && parsedPackage.getSigningDetails().getSignatureSchemeVersion()
< SignatureSchemeVersion.SIGNING_BLOCK_V2) {
Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName()
+ " is not signed with at least APK Signature Scheme v2");
@@ -23392,25 +23385,6 @@
getPackageFromComponentString(R.string.config_defaultRotationResolverService));
}
- private @Nullable String getDocumenterPackageName() {
- final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
- intent.addCategory(Intent.CATEGORY_OPENABLE);
- intent.setType("*/*");
- final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver());
-
- final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, resolvedType,
- MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE
- | MATCH_DISABLED_COMPONENTS,
- UserHandle.myUserId());
- if (matches.size() == 1) {
- return matches.get(0).getComponentInfo().packageName;
- } else {
- Slog.e(TAG, "There should probably be exactly one documenter; found "
- + matches.size() + ": matches=" + matches);
- return null;
- }
- }
-
@Nullable
private String getDeviceConfiguratorPackageName() {
return ensureSystemPackageName(mContext.getString(
@@ -23490,10 +23464,10 @@
final AndroidPackage androidPkg = mPackages.get(predefinedPkgName);
if (androidPkg != null) {
final SigningDetails signingDetail = androidPkg.getSigningDetails();
- if (signingDetail != null && signingDetail.signatures != null) {
+ if (signingDetail != null && signingDetail.getSignatures() != null) {
try {
final MessageDigest msgDigest = MessageDigest.getInstance("SHA-256");
- for (Signature signature : signingDetail.signatures) {
+ for (Signature signature : signingDetail.getSignatures()) {
if (TextUtils.equals(predefinedSignature,
HexEncoding.encodeToString(msgDigest.digest(
signature.toByteArray()), false))) {
@@ -26534,6 +26508,11 @@
private int verifyReplacingVersionCode(PackageInfoLite pkgLite,
long requiredInstalledVersionCode, int installFlags) {
+ if ((installFlags & PackageManager.INSTALL_APEX) != 0) {
+ return verifyReplacingVersionCodeForApex(
+ pkgLite, requiredInstalledVersionCode, installFlags);
+ }
+
String packageName = pkgLite.packageName;
synchronized (mLock) {
// Package which currently owns the data that the new package will own if installed.
@@ -26580,6 +26559,40 @@
return PackageManager.INSTALL_SUCCEEDED;
}
+ private int verifyReplacingVersionCodeForApex(PackageInfoLite pkgLite,
+ long requiredInstalledVersionCode, int installFlags) {
+ String packageName = pkgLite.packageName;
+
+ final PackageInfo activePackage = mApexManager.getPackageInfo(packageName,
+ ApexManager.MATCH_ACTIVE_PACKAGE);
+ if (activePackage == null) {
+ Slog.w(TAG, "Attempting to install new APEX package " + packageName);
+ return PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION;
+ }
+
+ final long activeVersion = activePackage.getLongVersionCode();
+ if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST
+ && activeVersion != requiredInstalledVersionCode) {
+ Slog.w(TAG, "Installed version of APEX package " + packageName
+ + " does not match required. Active version: " + activeVersion
+ + " required: " + requiredInstalledVersionCode);
+ return PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION;
+ }
+
+ final boolean isAppDebuggable = (activePackage.applicationInfo.flags
+ & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+ final long newVersionCode = pkgLite.getLongVersionCode();
+ if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags, isAppDebuggable)
+ && newVersionCode < activeVersion) {
+ Slog.w(TAG, "Downgrade of APEX package " + packageName
+ + " is not allowed. Active version: " + activeVersion
+ + " attempted: " + newVersionCode);
+ return PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
+ }
+
+ return PackageManager.INSTALL_SUCCEEDED;
+ }
+
/**
* Check and throw if the given before/after packages would be considered a
* downgrade.
@@ -26908,7 +26921,7 @@
}
return pkg.getSigningDetails().hasAncestorOrSelf(mPlatformPackage.getSigningDetails())
|| mPlatformPackage.getSigningDetails().checkCapability(pkg.getSigningDetails(),
- PackageParser.SigningDetails.CertCapabilities.PERMISSION);
+ SigningDetails.CertCapabilities.PERMISSION);
}
@Override
@@ -27085,8 +27098,6 @@
mDefaultTextClassifierPackage, mSystemTextClassifierPackageName);
case PackageManagerInternal.PACKAGE_PERMISSION_CONTROLLER:
return filterOnlySystemPackages(mRequiredPermissionControllerPackage);
- case PackageManagerInternal.PACKAGE_DOCUMENTER:
- return filterOnlySystemPackages(mDocumenterPackage);
case PackageManagerInternal.PACKAGE_CONFIGURATOR:
return filterOnlySystemPackages(mConfiguratorPackage);
case PackageManagerInternal.PACKAGE_INCIDENT_REPORT_APPROVER:
@@ -28753,6 +28764,56 @@
}
}
}
+
+ @Override
+ public IntentSender getLaunchIntentSenderForPackage(String packageName, String callingPackage,
+ String featureId, int userId) throws RemoteException {
+ Objects.requireNonNull(packageName);
+ final int callingUid = Binder.getCallingUid();
+ enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
+ false /* checkShell */, "get launch intent sender for package");
+ final int packageUid = getPackageUid(callingPackage, 0 /* flags */, userId);
+ if (!UserHandle.isSameApp(callingUid, packageUid)) {
+ throw new SecurityException("getLaunchIntentSenderForPackage() from calling uid: "
+ + callingUid + " does not own package: " + callingPackage);
+ }
+
+ // Using the same implementation with the #getLaunchIntentForPackage to get the ResolveInfo.
+ // Pass the resolveForStart as true in queryIntentActivities to skip the app filtering.
+ final Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
+ intentToResolve.addCategory(Intent.CATEGORY_INFO);
+ intentToResolve.setPackage(packageName);
+ String resolvedType = intentToResolve.resolveTypeIfNeeded(mContext.getContentResolver());
+ List<ResolveInfo> ris = queryIntentActivitiesInternal(intentToResolve, resolvedType,
+ 0 /* flags */, 0 /* privateResolveFlags */, callingUid, userId,
+ true /* resolveForStart */, false /* allowDynamicSplits */);
+ if (ris == null || ris.size() <= 0) {
+ intentToResolve.removeCategory(Intent.CATEGORY_INFO);
+ intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
+ intentToResolve.setPackage(packageName);
+ resolvedType = intentToResolve.resolveTypeIfNeeded(mContext.getContentResolver());
+ ris = queryIntentActivitiesInternal(intentToResolve, resolvedType,
+ 0 /* flags */, 0 /* privateResolveFlags */, callingUid, userId,
+ true /* resolveForStart */, false /* allowDynamicSplits */);
+ }
+
+ final Intent intent = new Intent(intentToResolve);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ // For the case of empty result, no component name is assigned into the intent. A
+ // non-launchable IntentSender which contains the failed intent is created. The
+ // SendIntentException is thrown if the IntentSender#sendIntent is invoked.
+ if (ris != null && !ris.isEmpty()) {
+ intent.setClassName(ris.get(0).activityInfo.packageName,
+ ris.get(0).activityInfo.name);
+ }
+ final IIntentSender target = ActivityManager.getService().getIntentSenderWithFeature(
+ ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
+ featureId, null /* token */, null /* resultWho */,
+ 1 /* requestCode */, new Intent[] { intent },
+ resolvedType != null ? new String[] { resolvedType } : null,
+ PendingIntent.FLAG_IMMUTABLE, null /* bOptions */, userId);
+ return new IntentSender(target);
+ }
}
interface PackageSender {
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 8b5abf3..5fdb57d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -34,9 +34,9 @@
import android.content.Intent;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
import android.content.pm.ResolveInfo;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.content.pm.parsing.PackageLite;
import android.content.pm.parsing.result.ParseResult;
@@ -499,9 +499,9 @@
*/
public static boolean comparePackageSignatures(PackageSetting pkgSetting,
Signature[] signatures) {
- return pkgSetting.signatures.mSigningDetails
- == PackageParser.SigningDetails.UNKNOWN
- || compareSignatures(pkgSetting.signatures.mSigningDetails.signatures, signatures)
+ final SigningDetails signingDetails = pkgSetting.signatures.mSigningDetails;
+ return signingDetails == SigningDetails.UNKNOWN
+ || compareSignatures(signingDetails.getSignatures(), signatures)
== PackageManager.SIGNATURE_MATCH;
}
@@ -512,13 +512,13 @@
* system upgrade) and {@code scannedSigs} will be in the newer format.
*/
private static boolean matchSignaturesCompat(String packageName,
- PackageSignatures packageSignatures, PackageParser.SigningDetails parsedSignatures) {
+ PackageSignatures packageSignatures, SigningDetails parsedSignatures) {
ArraySet<Signature> existingSet = new ArraySet<Signature>();
- for (Signature sig : packageSignatures.mSigningDetails.signatures) {
+ for (Signature sig : packageSignatures.mSigningDetails.getSignatures()) {
existingSet.add(sig);
}
ArraySet<Signature> scannedCompatSet = new ArraySet<Signature>();
- for (Signature sig : parsedSignatures.signatures) {
+ for (Signature sig : parsedSignatures.getSignatures()) {
try {
Signature[] chainSignatures = sig.getChainSignatures();
for (Signature chainSig : chainSignatures) {
@@ -547,9 +547,9 @@
private static boolean matchSignaturesRecover(
String packageName,
- PackageParser.SigningDetails existingSignatures,
- PackageParser.SigningDetails parsedSignatures,
- @PackageParser.SigningDetails.CertCapabilities int flags) {
+ SigningDetails existingSignatures,
+ SigningDetails parsedSignatures,
+ @SigningDetails.CertCapabilities int flags) {
String msg = null;
try {
if (parsedSignatures.checkCapabilityRecover(existingSignatures, flags)) {
@@ -576,10 +576,10 @@
PackageSetting disabledPkgSetting) {
if (pkgSetting.signatures.mSigningDetails.checkCapability(
disabledPkgSetting.signatures.mSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
+ SigningDetails.CertCapabilities.INSTALLED_DATA)
|| disabledPkgSetting.signatures.mSigningDetails.checkCapability(
pkgSetting.signatures.mSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) {
+ SigningDetails.CertCapabilities.ROLLBACK)) {
return true;
} else {
logCriticalInfo(Log.ERROR, "Updated system app mismatches cert on /system: " +
@@ -623,19 +623,19 @@
* @throws PackageManagerException if the signatures did not match.
*/
public static boolean verifySignatures(PackageSetting pkgSetting,
- PackageSetting disabledPkgSetting, PackageParser.SigningDetails parsedSignatures,
+ PackageSetting disabledPkgSetting, SigningDetails parsedSignatures,
boolean compareCompat, boolean compareRecover, boolean isRollback)
throws PackageManagerException {
final String packageName = pkgSetting.name;
boolean compatMatch = false;
- if (pkgSetting.signatures.mSigningDetails.signatures != null) {
+ if (pkgSetting.signatures.mSigningDetails.getSignatures() != null) {
// Already existing package. Make sure signatures match
boolean match = parsedSignatures.checkCapability(
pkgSetting.signatures.mSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
+ SigningDetails.CertCapabilities.INSTALLED_DATA)
|| pkgSetting.signatures.mSigningDetails.checkCapability(
parsedSignatures,
- PackageParser.SigningDetails.CertCapabilities.ROLLBACK);
+ SigningDetails.CertCapabilities.ROLLBACK);
if (!match && compareCompat) {
match = matchSignaturesCompat(packageName, pkgSetting.signatures,
parsedSignatures);
@@ -646,12 +646,12 @@
packageName,
pkgSetting.signatures.mSigningDetails,
parsedSignatures,
- PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
+ SigningDetails.CertCapabilities.INSTALLED_DATA)
|| matchSignaturesRecover(
packageName,
parsedSignatures,
pkgSetting.signatures.mSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.ROLLBACK);
+ SigningDetails.CertCapabilities.ROLLBACK);
}
if (!match && isApkVerificationForced(disabledPkgSetting)) {
@@ -674,7 +674,7 @@
// Check for shared user signatures
if (pkgSetting.getSharedUser() != null
&& pkgSetting.getSharedUser().signatures.mSigningDetails
- != PackageParser.SigningDetails.UNKNOWN) {
+ != SigningDetails.UNKNOWN) {
// Already existing package. Make sure signatures match. In case of signing certificate
// rotation, the packages with newer certs need to be ok with being sharedUserId with
@@ -684,10 +684,10 @@
boolean match =
parsedSignatures.checkCapability(
pkgSetting.getSharedUser().signatures.mSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID)
+ SigningDetails.CertCapabilities.SHARED_USER_ID)
|| pkgSetting.getSharedUser().signatures.mSigningDetails.checkCapability(
parsedSignatures,
- PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID);
+ SigningDetails.CertCapabilities.SHARED_USER_ID);
// Special case: if the sharedUserId capability check failed it could be due to this
// being the only package in the sharedUserId so far and the lineage being updated to
// deny the sharedUserId capability of the previous key in the lineage.
@@ -704,11 +704,11 @@
matchSignaturesRecover(packageName,
pkgSetting.getSharedUser().signatures.mSigningDetails,
parsedSignatures,
- PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID)
+ SigningDetails.CertCapabilities.SHARED_USER_ID)
|| matchSignaturesRecover(packageName,
parsedSignatures,
pkgSetting.getSharedUser().signatures.mSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID);
+ SigningDetails.CertCapabilities.SHARED_USER_ID);
compatMatch |= match;
}
if (!match) {
@@ -729,13 +729,13 @@
if (packageName.equals(shUidPkgSetting.name)) {
continue;
}
- PackageParser.SigningDetails shUidSigningDetails =
+ SigningDetails shUidSigningDetails =
shUidPkgSetting.getSigningDetails();
// The capability check only needs to be performed against the package if it is
// signed with a key that is in the lineage of the package being installed.
if (parsedSignatures.hasAncestor(shUidSigningDetails)) {
if (!parsedSignatures.checkCapability(shUidSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID)) {
+ SigningDetails.CertCapabilities.SHARED_USER_ID)) {
throw new PackageManagerException(
INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
"Package " + packageName
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 731d41c..e2443c1 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -27,9 +27,9 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.IncrementalStatesInfo;
import android.content.pm.PackageManager.UninstallReason;
-import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.SuspendDialogInfo;
import android.content.pm.overlay.OverlayPaths;
import android.os.PersistableBundle;
@@ -217,10 +217,10 @@
}
public Signature[] getSignatures() {
- return signatures.mSigningDetails.signatures;
+ return signatures.mSigningDetails.getSignatures();
}
- public PackageParser.SigningDetails getSigningDetails() {
+ public SigningDetails getSigningDetails() {
return signatures.mSigningDetails;
}
diff --git a/services/core/java/com/android/server/pm/PackageSignatures.java b/services/core/java/com/android/server/pm/PackageSignatures.java
index 394cdee..a1e5051 100644
--- a/services/core/java/com/android/server/pm/PackageSignatures.java
+++ b/services/core/java/com/android/server/pm/PackageSignatures.java
@@ -17,9 +17,9 @@
package com.android.server.pm;
import android.annotation.NonNull;
-import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
+import android.content.pm.SigningDetails.SignatureSchemeVersion;
import android.util.Log;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
@@ -35,40 +35,41 @@
class PackageSignatures {
- @NonNull PackageParser.SigningDetails mSigningDetails;
+ @NonNull SigningDetails mSigningDetails;
PackageSignatures(PackageSignatures orig) {
- if (orig != null && orig.mSigningDetails != PackageParser.SigningDetails.UNKNOWN) {
- mSigningDetails = new PackageParser.SigningDetails(orig.mSigningDetails);
+ if (orig != null && orig.mSigningDetails != SigningDetails.UNKNOWN) {
+ mSigningDetails = new SigningDetails(orig.mSigningDetails);
} else {
- mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
+ mSigningDetails = SigningDetails.UNKNOWN;
}
}
- PackageSignatures(PackageParser.SigningDetails signingDetails) {
+ PackageSignatures(SigningDetails signingDetails) {
mSigningDetails = signingDetails;
}
PackageSignatures() {
- mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
+ mSigningDetails = SigningDetails.UNKNOWN;
}
void writeXml(TypedXmlSerializer serializer, String tagName,
ArrayList<Signature> writtenSignatures) throws IOException {
- if (mSigningDetails.signatures == null) {
+ if (mSigningDetails.getSignatures() == null) {
return;
}
serializer.startTag(null, tagName);
- serializer.attributeInt(null, "count", mSigningDetails.signatures.length);
- serializer.attributeInt(null, "schemeVersion", mSigningDetails.signatureSchemeVersion);
- writeCertsListXml(serializer, writtenSignatures, mSigningDetails.signatures, false);
+ serializer.attributeInt(null, "count", mSigningDetails.getSignatures().length);
+ serializer.attributeInt(null, "schemeVersion", mSigningDetails.getSignatureSchemeVersion());
+ writeCertsListXml(serializer, writtenSignatures, mSigningDetails.getSignatures(), false);
// if we have past signer certificate information, write it out
- if (mSigningDetails.pastSigningCertificates != null) {
+ if (mSigningDetails.getPastSigningCertificates() != null) {
serializer.startTag(null, "pastSigs");
- serializer.attributeInt(null, "count", mSigningDetails.pastSigningCertificates.length);
+ serializer.attributeInt(null, "count",
+ mSigningDetails.getPastSigningCertificates().length);
writeCertsListXml(serializer, writtenSignatures,
- mSigningDetails.pastSigningCertificates, true);
+ mSigningDetails.getPastSigningCertificates(), true);
serializer.endTag(null, "pastSigs");
}
serializer.endTag(null, tagName);
@@ -106,8 +107,7 @@
void readXml(TypedXmlPullParser parser, ArrayList<Signature> readSignatures)
throws IOException, XmlPullParserException {
- PackageParser.SigningDetails.Builder builder =
- new PackageParser.SigningDetails.Builder();
+ SigningDetails.Builder builder = new SigningDetails.Builder();
final int count = parser.getAttributeInt(null, "count", -1);
if (count == -1) {
@@ -142,13 +142,13 @@
PackageManagerService.reportSettingsProblem(Log.WARN,
"Error in package manager settings: <sigs> "
+ "unable to convert certificate(s) to public key(s).");
- mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
+ mSigningDetails = SigningDetails.UNKNOWN;
}
}
private int readCertsListXml(TypedXmlPullParser parser, ArrayList<Signature> readSignatures,
ArrayList<Signature> signatures, int count, boolean isPastSigs,
- PackageParser.SigningDetails.Builder builder)
+ SigningDetails.Builder builder)
throws IOException, XmlPullParserException {
int pos = 0;
@@ -306,25 +306,25 @@
buf.append("PackageSignatures{");
buf.append(Integer.toHexString(System.identityHashCode(this)));
buf.append(" version:");
- buf.append(mSigningDetails.signatureSchemeVersion);
+ buf.append(mSigningDetails.getSignatureSchemeVersion());
buf.append(", signatures:[");
- if (mSigningDetails.signatures != null) {
- for (int i = 0; i < mSigningDetails.signatures.length; i++) {
+ if (mSigningDetails.getSignatures() != null) {
+ for (int i = 0; i < mSigningDetails.getSignatures().length; i++) {
if (i > 0) buf.append(", ");
buf.append(Integer.toHexString(
- mSigningDetails.signatures[i].hashCode()));
+ mSigningDetails.getSignatures()[i].hashCode()));
}
}
buf.append("]");
buf.append(", past signatures:[");
- if (mSigningDetails.pastSigningCertificates != null) {
- for (int i = 0; i < mSigningDetails.pastSigningCertificates.length; i++) {
+ if (mSigningDetails.getPastSigningCertificates() != null) {
+ for (int i = 0; i < mSigningDetails.getPastSigningCertificates().length; i++) {
if (i > 0) buf.append(", ");
buf.append(Integer.toHexString(
- mSigningDetails.pastSigningCertificates[i].hashCode()));
+ mSigningDetails.getPastSigningCertificates()[i].hashCode()));
buf.append(" flags: ");
- buf.append(
- Integer.toHexString(mSigningDetails.pastSigningCertificates[i].getFlags()));
+ buf.append(Integer.toHexString(
+ mSigningDetails.getPastSigningCertificates()[i].getFlags()));
}
}
buf.append("]}");
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index c5fbfba..f078c49 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -19,8 +19,8 @@
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageParser.SigningDetails;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.os.Environment;
import android.util.Slog;
import android.util.Xml;
@@ -79,7 +79,7 @@
/**
* Allows opt-in to the latest targetSdkVersion enforced changes without changing target SDK.
- * Turning this change off for an app targeting the latest SDK is a no-op.
+ * Turning this change off for an app targeting >= the latest SDK is a no-op.
*
* <p>Has no effect for apps using shared user id.
*
@@ -92,7 +92,7 @@
/**
* This change gates apps access to untrusted_app_R-targetSDK SELinux domain. Allows opt-in
* to R targetSdkVersion enforced changes without changing target SDK. Turning this change
- * off for an app targeting S is a no-op.
+ * off for an app targeting >= S is a no-op.
*
* <p>Has no effect for apps using shared user id.
*
@@ -364,7 +364,7 @@
}
final ApplicationInfo appInfo = pkg.toAppInfoWithoutState();
if (compatibility.isChangeEnabledInternal(SELINUX_LATEST_CHANGES, appInfo)) {
- return android.os.Build.VERSION_CODES.S;
+ return Math.max(android.os.Build.VERSION_CODES.S, pkg.getTargetSdkVersion());
} else if (compatibility.isChangeEnabledInternal(SELINUX_R_CHANGES, appInfo)) {
return Math.max(android.os.Build.VERSION_CODES.R, pkg.getTargetSdkVersion());
}
@@ -577,7 +577,7 @@
// Check for exact signature matches across all certs.
Signature[] certs = mCerts.toArray(new Signature[0]);
if (pkg.getSigningDetails() != SigningDetails.UNKNOWN
- && !Signature.areExactMatch(certs, pkg.getSigningDetails().signatures)) {
+ && !Signature.areExactMatch(certs, pkg.getSigningDetails().getSignatures())) {
// certs aren't exact match, but the package may have rotated from the known system cert
if (certs.length > 1 || !pkg.getSigningDetails().hasCertificate(certs[0])) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 7aa1c3a..0f532b6 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -1169,12 +1169,13 @@
// by that time.
void insertPackageSettingLPw(PackageSetting p, AndroidPackage pkg) {
// Update signatures if needed.
- if (p.signatures.mSigningDetails.signatures == null) {
+ if (p.signatures.mSigningDetails.getSignatures() == null) {
p.signatures.mSigningDetails = pkg.getSigningDetails();
}
// If this app defines a shared user id initialize
// the shared user signatures as well.
- if (p.sharedUser != null && p.sharedUser.signatures.mSigningDetails.signatures == null) {
+ if (p.sharedUser != null
+ && p.sharedUser.signatures.mSigningDetails.getSignatures() == null) {
p.sharedUser.signatures.mSigningDetails = pkg.getSigningDetails();
}
addPackageSettingLPw(p, p.sharedUser);
@@ -2966,6 +2967,17 @@
mReadMessages.append("Error reading: " + e.toString());
PackageManagerService.reportSettingsProblem(Log.ERROR, "Error reading settings: " + e);
Slog.wtf(PackageManagerService.TAG, "Error reading package manager settings", e);
+ } finally {
+ if (!mVersion.containsKey(StorageManager.UUID_PRIVATE_INTERNAL)) {
+ Slog.wtf(PackageManagerService.TAG,
+ "No internal VersionInfo found in settings, using current.");
+ findOrCreateVersion(StorageManager.UUID_PRIVATE_INTERNAL).forceCurrent();
+ }
+ if (!mVersion.containsKey(StorageManager.UUID_PRIMARY_PHYSICAL)) {
+ Slog.wtf(PackageManagerService.TAG,
+ "No external VersionInfo found in settings, using current.");
+ findOrCreateVersion(StorageManager.UUID_PRIMARY_PHYSICAL).forceCurrent();
+ }
}
// If the build is setup to drop runtime permissions
@@ -4497,7 +4509,7 @@
pw.print(prefix); pw.print(" versionName="); pw.println(pkg.getVersionName());
pw.print(prefix); pw.print(" usesNonSdkApi="); pw.println(pkg.isUsesNonSdkApi());
pw.print(prefix); pw.print(" splits="); dumpSplitNames(pw, pkg); pw.println();
- final int apkSigningVersion = pkg.getSigningDetails().signatureSchemeVersion;
+ final int apkSigningVersion = pkg.getSigningDetails().getSignatureSchemeVersion();
pw.print(prefix); pw.print(" apkSigningVersion="); pw.println(apkSigningVersion);
pw.print(prefix); pw.print(" applicationInfo=");
pw.println(pkg.toAppInfoToString());
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 5f10277..0e92b3e 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -86,6 +86,7 @@
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.SystemClock;
+import android.os.Trace;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.text.TextUtils;
@@ -598,6 +599,7 @@
if (DEBUG_PROCSTATE) {
Slog.d(TAG, "onUidStateChanged: uid=" + uid + " state=" + procState);
}
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "shortcutHandleOnUidStateChanged");
synchronized (mLock) {
mUidState.put(uid, procState);
@@ -608,6 +610,7 @@
mUidLastForegroundElapsedTime.put(uid, injectElapsedRealtime());
}
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
private boolean isProcessStateForeground(int processState) {
@@ -703,10 +706,12 @@
// or anything.
final long start = getStatStartTime();
injectRunOnNewThread(() -> {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "shortcutHandleUnlockUser");
synchronized (mLock) {
logDurationStat(Stats.ASYNC_PRELOAD_USER_DELAY, start);
getUserShortcutsLocked(userId);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
});
}
@@ -715,6 +720,7 @@
if (DEBUG || DEBUG_REBOOT) {
Slog.d(TAG, "handleStopUser: user=" + userId);
}
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "shortcutHandleStopUser");
synchronized (mLock) {
unloadUserLocked(userId);
@@ -722,6 +728,7 @@
mUnlockedUsers.put(userId, false);
}
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
@GuardedBy("mLock")
@@ -1192,6 +1199,7 @@
return;
}
try {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "shortcutSaveDirtyInfo");
synchronized (mLock) {
for (int i = mDirtyUserIds.size() - 1; i >= 0; i--) {
final int userId = mDirtyUserIds.get(i);
@@ -1205,6 +1213,8 @@
}
} catch (Exception e) {
wtf("Exception in saveDirtyInfo", e);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
}
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 4a68b76..d4129ec 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -28,7 +28,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
@@ -36,8 +35,8 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.PackageParser.SigningDetails;
-import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
+import android.content.pm.SigningDetails;
+import android.content.pm.SigningDetails.SignatureSchemeVersion;
import android.content.pm.parsing.PackageInfoWithoutStateUtils;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
@@ -82,6 +81,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
@@ -114,6 +114,8 @@
@GuardedBy("mSuccessfulStagedSessionIds")
private final List<Integer> mSuccessfulStagedSessionIds = new ArrayList<>();
+ private final CompletableFuture<Void> mBootCompleted = new CompletableFuture<>();
+
interface StagedSession {
boolean isMultiPackage();
boolean isApexSession();
@@ -138,25 +140,28 @@
boolean hasParentSessionId();
long getCommittedMillis();
void abandon();
- boolean notifyStartPreRebootVerification();
void notifyEndPreRebootVerification();
void verifySession();
}
- StagingManager(Context context, Supplier<PackageParser2> packageParserSupplier) {
- this(context, packageParserSupplier, ApexManager.getInstance());
+ StagingManager(Context context, Supplier<PackageParser2> packageParserSupplier, Looper looper) {
+ this(context, packageParserSupplier, ApexManager.getInstance(), looper);
}
@VisibleForTesting
StagingManager(Context context, Supplier<PackageParser2> packageParserSupplier,
ApexManager apexManager) {
+ this(context, packageParserSupplier, apexManager, BackgroundThread.get().getLooper());
+ }
+
+ StagingManager(Context context, Supplier<PackageParser2> packageParserSupplier,
+ ApexManager apexManager, Looper looper) {
mContext = context;
mPackageParserSupplier = packageParserSupplier;
mApexManager = apexManager;
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
- mPreRebootVerificationHandler = new PreRebootVerificationHandler(
- BackgroundThread.get().getLooper());
+ mPreRebootVerificationHandler = new PreRebootVerificationHandler(looper);
if (mFailureReasonFile.exists()) {
try (BufferedReader reader = new BufferedReader(new FileReader(mFailureReasonFile))) {
@@ -231,7 +236,7 @@
final SigningDetails existingSigningDetails;
try {
existingSigningDetails = ApkSignatureVerifier.verify(
- existingApexPkg.applicationInfo.sourceDir, SignatureSchemeVersion.JAR);
+ existingApexPkg.applicationInfo.sourceDir, SignatureSchemeVersion.JAR);
} catch (PackageParserException e) {
throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
"Failed to parse APEX package " + existingApexPkg.applicationInfo.sourceDir
@@ -296,15 +301,6 @@
throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
"Failed to parse APEX package " + apexInfo.modulePath + " : " + e, e);
}
- final PackageInfo activePackage = mApexManager.getPackageInfo(packageInfo.packageName,
- ApexManager.MATCH_ACTIVE_PACKAGE);
- if (activePackage == null) {
- Slog.w(TAG, "Attempting to install new APEX package " + packageInfo.packageName);
- throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
- "It is forbidden to install new APEX packages.");
- }
- checkRequiredVersionCode(session, activePackage);
- checkDowngrade(session, activePackage, packageInfo);
result.add(packageInfo);
apexPackageNames.add(packageInfo.packageName);
}
@@ -327,40 +323,6 @@
"Could not find rollback id for commit session: " + sessionId);
}
- private void checkRequiredVersionCode(final StagedSession session,
- final PackageInfo activePackage) throws PackageManagerException {
- if (session.sessionParams().requiredInstalledVersionCode
- == PackageManager.VERSION_CODE_HIGHEST) {
- return;
- }
- final long activeVersion = activePackage.applicationInfo.longVersionCode;
- if (activeVersion != session.sessionParams().requiredInstalledVersionCode) {
- throw new PackageManagerException(
- SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
- "Installed version of APEX package " + activePackage.packageName
- + " does not match required. Active version: " + activeVersion
- + " required: " + session.sessionParams().requiredInstalledVersionCode);
- }
- }
-
- private void checkDowngrade(final StagedSession session,
- final PackageInfo activePackage, final PackageInfo newPackage)
- throws PackageManagerException {
- final long activeVersion = activePackage.applicationInfo.longVersionCode;
- final long newVersionCode = newPackage.applicationInfo.longVersionCode;
- final boolean isAppDebuggable = (activePackage.applicationInfo.flags
- & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
- final boolean allowsDowngrade = PackageManagerServiceUtils.isDowngradePermitted(
- session.sessionParams().installFlags, isAppDebuggable);
- if (activeVersion > newVersionCode && !allowsDowngrade) {
- throw new PackageManagerException(
- SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
- "Downgrade of APEX package " + newPackage.packageName
- + " is not allowed. Active version: " + activeVersion
- + " attempted: " + newVersionCode);
- }
- }
-
// Reverts apex sessions and user data (if checkpoint is supported). Also reboots the device.
private void abortCheckpoint(String failureReason, boolean supportsCheckpoint,
boolean needsCheckpoint) {
@@ -869,7 +831,8 @@
} else if (!session.isSessionReady()) {
// The framework got restarted before the pre-reboot verification could complete,
// restart the verification.
- mPreRebootVerificationHandler.startPreRebootVerification(session);
+ Slog.i(TAG, "Restart verification for session=" + session.sessionId());
+ mBootCompleted.thenRun(() -> session.verifySession());
StagedSession session2 = sessions.set(j - 1, session);
sessions.set(i, session2);
j--;
@@ -1054,7 +1017,7 @@
@VisibleForTesting
void onBootCompletedBroadcastReceived() {
- mPreRebootVerificationHandler.readyToStart();
+ mBootCompleted.complete(null);
BackgroundThread.getExecutor().execute(() -> logFailedApexSessionsIfNecessary());
}
@@ -1095,24 +1058,7 @@
return session;
}
- // TODO(b/136257624): Temporary API to let PMS communicate with StagingManager. When all
- // verification logic is extracted out of StagingManager into PMS, we can remove
- // this.
- void notifyVerificationComplete(StagedSession session) {
- mPreRebootVerificationHandler.onPreRebootVerificationComplete(session);
- }
-
- // TODO(b/136257624): Temporary API to let PMS communicate with StagingManager. When all
- // verification logic is extracted out of StagingManager into PMS, we can remove
- // this.
- void notifyPreRebootVerification_Apk_Complete(@NonNull StagedSession session) {
- mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete(session);
- }
-
private final class PreRebootVerificationHandler extends Handler {
- // Hold sessions before handler gets ready to do the verification.
- private List<StagedSession> mPendingSessions;
- private boolean mIsReady;
PreRebootVerificationHandler(Looper looper) {
super(looper);
@@ -1126,7 +1072,6 @@
* <p><ul>
* <li>MSG_PRE_REBOOT_VERIFICATION_START</li>
* <li>MSG_PRE_REBOOT_VERIFICATION_APEX</li>
- * <li>MSG_PRE_REBOOT_VERIFICATION_APK</li>
* <li>MSG_PRE_REBOOT_VERIFICATION_END</li>
* </ul></p>
*
@@ -1134,8 +1079,7 @@
*/
private static final int MSG_PRE_REBOOT_VERIFICATION_START = 1;
private static final int MSG_PRE_REBOOT_VERIFICATION_APEX = 2;
- private static final int MSG_PRE_REBOOT_VERIFICATION_APK = 3;
- private static final int MSG_PRE_REBOOT_VERIFICATION_END = 4;
+ private static final int MSG_PRE_REBOOT_VERIFICATION_END = 3;
@Override
public void handleMessage(Message msg) {
@@ -1155,9 +1099,6 @@
case MSG_PRE_REBOOT_VERIFICATION_APEX:
handlePreRebootVerification_Apex(session, rollbackId);
break;
- case MSG_PRE_REBOOT_VERIFICATION_APK:
- handlePreRebootVerification_Apk(session);
- break;
case MSG_PRE_REBOOT_VERIFICATION_END:
handlePreRebootVerification_End(session);
break;
@@ -1170,35 +1111,15 @@
}
}
- // Notify the handler that system is ready, and reschedule the pre-reboot verifications.
- private synchronized void readyToStart() {
- mIsReady = true;
- if (mPendingSessions != null) {
- for (int i = 0; i < mPendingSessions.size(); i++) {
- StagedSession session = mPendingSessions.get(i);
- startPreRebootVerification(session);
- }
- mPendingSessions = null;
- }
- }
-
// Method for starting the pre-reboot verification
private synchronized void startPreRebootVerification(
@NonNull StagedSession session) {
- if (!mIsReady) {
- if (mPendingSessions == null) {
- mPendingSessions = new ArrayList<>();
- }
- mPendingSessions.add(session);
- return;
- }
-
- if (session.notifyStartPreRebootVerification()) {
+ mBootCompleted.thenRun(() -> {
int sessionId = session.sessionId();
Slog.d(TAG, "Starting preRebootVerification for session " + sessionId);
obtainMessage(MSG_PRE_REBOOT_VERIFICATION_START, sessionId, -1, session)
.sendToTarget();
- }
+ });
}
private void onPreRebootVerificationFailure(StagedSession session,
@@ -1227,12 +1148,6 @@
private void notifyPreRebootVerification_Apex_Complete(
@NonNull StagedSession session) {
- obtainMessage(MSG_PRE_REBOOT_VERIFICATION_APK, session.sessionId(), -1, session)
- .sendToTarget();
- }
-
- private void notifyPreRebootVerification_Apk_Complete(
- @NonNull StagedSession session) {
obtainMessage(MSG_PRE_REBOOT_VERIFICATION_END, session.sessionId(), -1, session)
.sendToTarget();
}
@@ -1320,19 +1235,6 @@
}
/**
- * Pre-reboot verification state for apk files. Session is sent to
- * {@link PackageManagerService} for verification and it notifies back the result via
- * {@link #notifyPreRebootVerification_Apk_Complete}
- */
- private void handlePreRebootVerification_Apk(@NonNull StagedSession session) {
- if (!session.containsApkSession()) {
- notifyPreRebootVerification_Apk_Complete(session);
- return;
- }
- session.verifySession();
- }
-
- /**
* Pre-reboot verification state for wrapping up:
* <p><ul>
* <li>enables rollback if required</li>
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
index 471a4d3..0d2bcec 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
@@ -21,8 +21,8 @@
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
-import android.content.pm.PackageParser;
import android.content.pm.PermissionGroupInfo;
+import android.content.pm.SigningDetails;
import android.content.pm.parsing.ParsingPackageRead;
import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.component.ParsedAttribution;
@@ -200,7 +200,7 @@
* The signature data of all APKs in this package, which must be exactly the same across the
* base and splits.
*/
- PackageParser.SigningDetails getSigningDetails();
+ SigningDetails getSigningDetails();
/**
* TODO(b/135203078): Move split stuff to an inner data class
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
index 5ee612b..b30c9da 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
@@ -196,6 +196,12 @@
}
}
+ if (pkg.getBackupAgentName() != null) {
+ if (Objects.equals(className, pkg.getBackupAgentName())) {
+ return true;
+ }
+ }
+
return false;
}
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
index 7fbe953..2234022 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
@@ -22,7 +22,7 @@
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
-import android.content.pm.PackageParser;
+import android.content.pm.SigningDetails;
import android.content.pm.parsing.ParsingPackage;
import android.content.pm.parsing.ParsingPackageImpl;
import android.content.pm.parsing.component.ParsedActivity;
@@ -252,7 +252,7 @@
}
@Override
- public PackageImpl setSigningDetails(@Nullable PackageParser.SigningDetails value) {
+ public PackageImpl setSigningDetails(@Nullable SigningDetails value) {
super.setSigningDetails(value);
return this;
}
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java b/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java
index 657f32c..8e4ee6a 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java
@@ -17,7 +17,7 @@
package com.android.server.pm.parsing.pkg;
import android.annotation.Nullable;
-import android.content.pm.PackageParser;
+import android.content.pm.SigningDetails;
/**
* Methods used for mutation after direct package parsing, mostly done inside
@@ -59,7 +59,7 @@
ParsedPackage setSecondaryCpuAbi(String secondaryCpuAbi);
- ParsedPackage setSigningDetails(PackageParser.SigningDetails signingDetails);
+ ParsedPackage setSigningDetails(SigningDetails signingDetails);
ParsedPackage setSplitCodePaths(String[] splitCodePaths);
diff --git a/services/core/java/com/android/server/pm/permission/Permission.java b/services/core/java/com/android/server/pm/permission/Permission.java
index cda4806..b1b46af 100644
--- a/services/core/java/com/android/server/pm/permission/Permission.java
+++ b/services/core/java/com/android/server/pm/permission/Permission.java
@@ -304,10 +304,6 @@
& PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER) != 0;
}
- public boolean isDocumenter() {
- return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_DOCUMENTER) != 0;
- }
-
public boolean isConfigurator() {
return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_CONFIGURATOR) != 0;
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index a391dbc..c474a91 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -84,12 +84,13 @@
import android.content.pm.PackageManager.PermissionInfoFlags;
import android.content.pm.PackageManager.PermissionWhitelistFlags;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
+import android.content.pm.SigningDetails;
import android.content.pm.parsing.component.ParsedPermission;
import android.content.pm.parsing.component.ParsedPermissionGroup;
+import android.content.pm.permission.CompatibilityPermissionInfo;
import android.content.pm.permission.SplitPermissionInfoParcelable;
import android.metrics.LogMaker;
import android.os.AsyncTask;
@@ -2827,7 +2828,7 @@
// Except... if this is a permission that was added
// to the platform (note: need to only do this when
// updating the platform).
- if (!isNewPlatformPermissionForPackage(perm, pkg)) {
+ if (!isCompatPlatformPermissionForPackage(perm, pkg)) {
shouldGrantNormalPermission = false;
}
}
@@ -3363,14 +3364,12 @@
return result;
}
- private static boolean isNewPlatformPermissionForPackage(String perm, AndroidPackage pkg) {
+ private static boolean isCompatPlatformPermissionForPackage(String perm, AndroidPackage pkg) {
boolean allowed = false;
- final int NP = PackageParser.NEW_PERMISSIONS.length;
- for (int ip=0; ip<NP; ip++) {
- final PackageParser.NewPermissionInfo npi
- = PackageParser.NEW_PERMISSIONS[ip];
- if (npi.name.equals(perm)
- && pkg.getTargetSdkVersion() < npi.sdkVersion) {
+ for (int i = 0, size = CompatibilityPermissionInfo.COMPAT_PERMS.length; i < size; i++) {
+ final CompatibilityPermissionInfo info = CompatibilityPermissionInfo.COMPAT_PERMS[i];
+ if (info.name.equals(perm)
+ && pkg.getTargetSdkVersion() < info.sdkVersion) {
allowed = true;
Log.i(TAG, "Auto-granting " + perm + " to old pkg "
+ pkg.getPackageName());
@@ -3490,15 +3489,15 @@
// - or it shares a common signing certificate in its lineage with the defining package,
// and the defining package still trusts the old certificate for permissions
// - or it shares the above relationships with the system package
- final PackageParser.SigningDetails sourceSigningDetails =
+ final SigningDetails sourceSigningDetails =
getSourcePackageSigningDetails(bp);
return sourceSigningDetails.hasCommonSignerWithCapability(
pkg.getSigningDetails(),
- PackageParser.SigningDetails.CertCapabilities.PERMISSION)
+ SigningDetails.CertCapabilities.PERMISSION)
|| pkg.getSigningDetails().hasAncestorOrSelf(systemPackage.getSigningDetails())
|| systemPackage.getSigningDetails().checkCapability(
pkg.getSigningDetails(),
- PackageParser.SigningDetails.CertCapabilities.PERMISSION);
+ SigningDetails.CertCapabilities.PERMISSION);
}
private boolean shouldGrantPermissionByProtectionFlags(@NonNull AndroidPackage pkg,
@@ -3608,14 +3607,6 @@
// Special permissions for the device configurator.
allowed = true;
}
- if (!allowed && bp.isDocumenter()
- && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
- PackageManagerInternal.PACKAGE_DOCUMENTER, UserHandle.USER_SYSTEM),
- pkg.getPackageName())) {
- // If this permission is to be granted to the documenter and
- // this app is the documenter, then it gets the permission.
- allowed = true;
- }
if (!allowed && bp.isIncidentReportApprover()
&& ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
PackageManagerInternal.PACKAGE_INCIDENT_REPORT_APPROVER,
@@ -3656,11 +3647,11 @@
}
@NonNull
- private PackageParser.SigningDetails getSourcePackageSigningDetails(
+ private SigningDetails getSourcePackageSigningDetails(
@NonNull Permission bp) {
final PackageSetting ps = getSourcePackageSetting(bp);
if (ps == null) {
- return PackageParser.SigningDetails.UNKNOWN;
+ return SigningDetails.UNKNOWN;
}
return ps.getSigningDetails();
}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
index adf8f0d..844111a 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
@@ -182,10 +182,10 @@
if (!reusedMap.isEmpty()) {
if (!wasHeaderPrinted) {
- Signature[] signatures = pkg.getSigningDetails().signatures;
+ Signature[] signatures = pkg.getSigningDetails().getSignatures();
String signaturesDigest = signatures == null ? null : Arrays.toString(
PackageUtils.computeSignaturesSha256Digests(
- pkg.getSigningDetails().signatures, ":"));
+ pkg.getSigningDetails().getSignatures(), ":"));
writer.println(pkgState.getPackageName() + ":");
writer.increaseIndent();
diff --git a/services/core/java/com/android/server/policy/LegacyGlobalActions.java b/services/core/java/com/android/server/policy/LegacyGlobalActions.java
index 5b48abb..a5969a8 100644
--- a/services/core/java/com/android/server/policy/LegacyGlobalActions.java
+++ b/services/core/java/com/android/server/policy/LegacyGlobalActions.java
@@ -135,7 +135,10 @@
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(TelephonyManager.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
- context.registerReceiver(mBroadcastReceiver, filter);
+ // By default CLOSE_SYSTEM_DIALOGS broadcast is sent only for current user, which is user
+ // 10 on devices with headless system user enabled.
+ // In order to receive the broadcast, register the broadcast receiver with UserHandle.ALL.
+ context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
mHasTelephony =
context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
diff --git a/services/core/java/com/android/server/policy/role/RoleServicePlatformHelperImpl.java b/services/core/java/com/android/server/policy/role/RoleServicePlatformHelperImpl.java
index c2596c7..c08faf8 100644
--- a/services/core/java/com/android/server/policy/role/RoleServicePlatformHelperImpl.java
+++ b/services/core/java/com/android/server/policy/role/RoleServicePlatformHelperImpl.java
@@ -323,7 +323,7 @@
dataOutputStream.writeUTF(disabledComponents.valueAt(i));
}
- for (final Signature signature : pkg.getSigningDetails().signatures) {
+ for (final Signature signature : pkg.getSigningDetails().getSignatures()) {
dataOutputStream.write(signature.toByteArray());
}
} catch (IOException e) {
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index 9560f59..7bf3478 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -116,10 +116,10 @@
static final int ROLLBACK_STATE_DELETED = 4;
/**
- * The session ID for the staged session if this rollback data represents a staged session,
- * {@code -1} otherwise.
+ * The session ID associate with this rollback. This is the parent session ID in the case of
+ * a multi-package session.
*/
- private final int mStagedSessionId;
+ private final int mOriginalSessionId;
/**
* The rollback info for this rollback.
@@ -181,13 +181,6 @@
private final int[] mPackageSessionIds;
/**
- * The number of sessions in the install which are notified with success by
- * {@link PackageInstaller.SessionCallback#onFinished(int, boolean)}.
- * This rollback will be enabled only after all child sessions finished with success.
- */
- private int mNumPackageSessionsWithSuccess;
-
- /**
* The extension versions supported at the time of rollback creation.
*/
@NonNull private final SparseIntArray mExtensionVersions;
@@ -203,24 +196,25 @@
*
* @param rollbackId the id of the rollback.
* @param backupDir the directory where the rollback data is stored.
- * @param stagedSessionId the session id if this is a staged rollback, -1 otherwise.
+ * @param originalSessionId the session id associated with this rollback.
+ * @param isStaged true if this is a staged rollback.
* @param userId the user that performed the install with rollback enabled.
* @param installerPackageName the installer package name from the original install session.
* @param packageSessionIds the session ids for all packages in the install.
* @param extensionVersions the extension versions supported at the time of rollback creation
*/
- Rollback(int rollbackId, File backupDir, int stagedSessionId, int userId,
+ Rollback(int rollbackId, File backupDir, int originalSessionId, boolean isStaged, int userId,
String installerPackageName, int[] packageSessionIds,
SparseIntArray extensionVersions) {
this.info = new RollbackInfo(rollbackId,
/* packages */ new ArrayList<>(),
- /* isStaged */ stagedSessionId != -1,
+ /* isStaged */ isStaged,
/* causePackages */ new ArrayList<>(),
/* committedSessionId */ -1);
mUserId = userId;
mInstallerPackageName = installerPackageName;
mBackupDir = backupDir;
- mStagedSessionId = stagedSessionId;
+ mOriginalSessionId = originalSessionId;
mState = ROLLBACK_STATE_ENABLING;
mTimestamp = Instant.now();
mPackageSessionIds = packageSessionIds != null ? packageSessionIds : new int[0];
@@ -228,16 +222,10 @@
mHandler = Looper.myLooper() != null ? new Handler(Looper.myLooper()) : null;
}
- Rollback(int rollbackId, File backupDir, int stagedSessionId, int userId,
- String installerPackageName) {
- this(rollbackId, backupDir, stagedSessionId, userId, installerPackageName, null,
- new SparseIntArray(0));
- }
-
/**
* Constructs a pre-populated Rollback instance.
*/
- Rollback(RollbackInfo info, File backupDir, Instant timestamp, int stagedSessionId,
+ Rollback(RollbackInfo info, File backupDir, Instant timestamp, int originalSessionId,
@RollbackState int state, String stateDescription, boolean restoreUserDataInProgress,
int userId, String installerPackageName, SparseIntArray extensionVersions) {
this.info = info;
@@ -245,7 +233,7 @@
mInstallerPackageName = installerPackageName;
mBackupDir = backupDir;
mTimestamp = timestamp;
- mStagedSessionId = stagedSessionId;
+ mOriginalSessionId = originalSessionId;
mState = state;
mStateDescription = stateDescription;
mRestoreUserDataInProgress = restoreUserDataInProgress;
@@ -298,12 +286,11 @@
}
/**
- * Returns the session ID for the staged session if this rollback data represents a staged
- * session, or {@code -1} otherwise.
+ * Returns the session ID associated with this rollback, or {@code -1} if unknown.
*/
@AnyThread
- int getStagedSessionId() {
- return mStagedSessionId;
+ int getOriginalSessionId() {
+ return mOriginalSessionId;
}
/**
@@ -838,17 +825,6 @@
}
/**
- * Called when a child session finished with success.
- * Returns true when all child sessions are notified with success. This rollback will be
- * enabled only after all child sessions finished with success.
- */
- @WorkerThread
- boolean notifySessionWithSuccess() {
- assertInWorkerThread();
- return ++mNumPackageSessionsWithSuccess == mPackageSessionIds.length;
- }
-
- /**
* Returns true if all packages in this rollback are enabled. We won't enable this rollback
* until all packages are enabled. Note we don't count apk-in-apex here since they are enabled
* automatically when the embedding apex is enabled.
@@ -954,9 +930,8 @@
ipw.println("-state: " + getStateAsString());
ipw.println("-stateDescription: " + mStateDescription);
ipw.println("-timestamp: " + getTimestamp());
- if (getStagedSessionId() != -1) {
- ipw.println("-stagedSessionId: " + getStagedSessionId());
- }
+ ipw.println("-isStaged: " + isStaged());
+ ipw.println("-originalSessionId: " + getOriginalSessionId());
ipw.println("-packages:");
ipw.increaseIndent();
for (PackageRollbackInfo pkg : info.getPackages()) {
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 9e19f57..f7ed000 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -247,11 +247,6 @@
PackageManagerInternal.class);
pm.setEnableRollbackCode(token, ret);
});
-
- // We're handling the ordered broadcast. Abort the
- // broadcast because there is no need for it to go to
- // anyone else.
- abortBroadcast();
}
}
}, enableRollbackFilter, null, getHandler());
@@ -611,11 +606,11 @@
}
PackageInstaller.SessionInfo session = mContext.getPackageManager()
- .getPackageInstaller().getSessionInfo(rollback.getStagedSessionId());
+ .getPackageInstaller().getSessionInfo(rollback.getOriginalSessionId());
if (session == null || session.isStagedSessionFailed()) {
if (rollback.isEnabling()) {
iter.remove();
- deleteRollback(rollback, "Session " + rollback.getStagedSessionId()
+ deleteRollback(rollback, "Session " + rollback.getOriginalSessionId()
+ " not existed or failed");
}
continue;
@@ -789,7 +784,13 @@
newRollback = createNewRollback(parentSession);
}
- return enableRollbackForPackageSession(newRollback, packageSession);
+ if (enableRollbackForPackageSession(newRollback, packageSession)) {
+ // Persist the rollback if all packages are enabled. We will make the rollback
+ // available once the whole session is installed successfully.
+ return newRollback.allPackagesEnabled() ? completeEnableRollback(newRollback) : true;
+ } else {
+ return false;
+ }
}
@WorkerThread
@@ -978,43 +979,10 @@
throw new SecurityException("notifyStagedSession may only be called by the system.");
}
- // NOTE: We post this runnable on the RollbackManager's binder thread because we'd prefer
- // to preserve the invariant that all operations that modify state happen there.
return awaitResult(() -> {
assertInWorkerThread();
- PackageInstaller installer = mContext.getPackageManager().getPackageInstaller();
-
- final PackageInstaller.SessionInfo session = installer.getSessionInfo(sessionId);
- if (session == null) {
- Slog.e(TAG, "No matching install session for: " + sessionId);
- return -1;
- }
-
- Rollback newRollback = createNewRollback(session);
- if (!session.isMultiPackage()) {
- if (!enableRollbackForPackageSession(newRollback, session)) {
- Slog.e(TAG, "Unable to enable rollback for session: " + sessionId);
- }
- } else {
- for (int childSessionId : session.getChildSessionIds()) {
- final PackageInstaller.SessionInfo childSession =
- installer.getSessionInfo(childSessionId);
- if (childSession == null) {
- Slog.e(TAG, "No matching child install session for: " + childSessionId);
- break;
- }
- if (!enableRollbackForPackageSession(newRollback, childSession)) {
- Slog.e(TAG, "Unable to enable rollback for session: " + sessionId);
- break;
- }
- }
- }
-
- if (!completeEnableRollback(newRollback)) {
- return -1;
- } else {
- return newRollback.info.getRollbackId();
- }
+ Rollback rollback = getRollbackForSession(sessionId);
+ return rollback != null ? rollback.info.getRollbackId() : -1;
});
}
@@ -1124,21 +1092,25 @@
Slog.v(TAG, "SessionCallback.onFinished id=" + sessionId + " success=" + success);
}
+ Rollback rollback = getRollbackForSession(sessionId);
+ if (rollback == null || !rollback.isEnabling()
+ || sessionId != rollback.getOriginalSessionId()) {
+ // We only care about the parent session id which will tell us whether the
+ // whole session is successful or not.
+ return;
+ }
if (success) {
- Rollback rollback = getRollbackForSession(sessionId);
- if (rollback != null && !rollback.isStaged() && rollback.isEnabling()
- && rollback.notifySessionWithSuccess()
- && completeEnableRollback(rollback)) {
+ if (!rollback.isStaged() && completeEnableRollback(rollback)) {
+ // completeEnableRollback() ensures the rollback is deleted if not all packages
+ // are enabled. For staged rollbacks, we will make them available in
+ // onBootCompleted().
makeRollbackAvailable(rollback);
}
} else {
- Rollback rollback = getRollbackForSession(sessionId);
- if (rollback != null && rollback.isEnabling()) {
- Slog.w(TAG, "Delete rollback id=" + rollback.info.getRollbackId()
- + " for failed session id=" + sessionId);
- mRollbacks.remove(rollback);
- deleteRollback(rollback, "Session " + sessionId + " failed");
- }
+ Slog.w(TAG, "Delete rollback id=" + rollback.info.getRollbackId()
+ + " for failed session id=" + sessionId);
+ mRollbacks.remove(rollback);
+ deleteRollback(rollback, "Session " + sessionId + " failed");
}
}
}
@@ -1307,7 +1279,7 @@
rollback = mRollbackStore.createStagedRollback(rollbackId, parentSessionId, userId,
installerPackageName, packageSessionIds, getExtensionVersions());
} else {
- rollback = mRollbackStore.createNonStagedRollback(rollbackId, userId,
+ rollback = mRollbackStore.createNonStagedRollback(rollbackId, parentSessionId, userId,
installerPackageName, packageSessionIds, getExtensionVersions());
}
@@ -1339,7 +1311,7 @@
// We expect mRollbacks to be a very small list; linear search should be plenty fast.
for (int i = 0; i < mRollbacks.size(); ++i) {
Rollback rollback = mRollbacks.get(i);
- if (rollback.getStagedSessionId() == sessionId
+ if (rollback.getOriginalSessionId() == sessionId
|| rollback.containsSessionId(sessionId)) {
return rollback;
}
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index fc62f5b..6b783f7 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -209,23 +209,24 @@
* Creates a new Rollback instance for a non-staged rollback with
* backupDir assigned.
*/
- Rollback createNonStagedRollback(int rollbackId, int userId, String installerPackageName,
- int[] packageSessionIds, SparseIntArray extensionVersions) {
+ Rollback createNonStagedRollback(int rollbackId, int originalSessionId, int userId,
+ String installerPackageName, int[] packageSessionIds,
+ SparseIntArray extensionVersions) {
File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
- return new Rollback(rollbackId, backupDir, -1, userId, installerPackageName,
- packageSessionIds, extensionVersions);
+ return new Rollback(rollbackId, backupDir, originalSessionId, /* isStaged */ false, userId,
+ installerPackageName, packageSessionIds, extensionVersions);
}
/**
* Creates a new Rollback instance for a staged rollback with
* backupDir assigned.
*/
- Rollback createStagedRollback(int rollbackId, int stagedSessionId, int userId,
+ Rollback createStagedRollback(int rollbackId, int originalSessionId, int userId,
String installerPackageName, int[] packageSessionIds,
SparseIntArray extensionVersions) {
File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
- return new Rollback(rollbackId, backupDir, stagedSessionId, userId, installerPackageName,
- packageSessionIds, extensionVersions);
+ return new Rollback(rollbackId, backupDir, originalSessionId, /* isStaged */ true, userId,
+ installerPackageName, packageSessionIds, extensionVersions);
}
private static boolean isLinkPossible(File oldFile, File newFile) {
@@ -312,7 +313,7 @@
JSONObject dataJson = new JSONObject();
dataJson.put("info", rollbackInfoToJson(rollback.info));
dataJson.put("timestamp", rollback.getTimestamp().toString());
- dataJson.put("stagedSessionId", rollback.getStagedSessionId());
+ dataJson.put("originalSessionId", rollback.getOriginalSessionId());
dataJson.put("state", rollback.getStateAsString());
dataJson.put("stateDescription", rollback.getStateDescription());
dataJson.put("restoreUserDataInProgress", rollback.isRestoreUserDataInProgress());
@@ -380,7 +381,9 @@
rollbackInfoFromJson(dataJson.getJSONObject("info")),
backupDir,
Instant.parse(dataJson.getString("timestamp")),
- dataJson.getInt("stagedSessionId"),
+ // Backward compatibility: Historical rollbacks are not erased upon OTA update.
+ // Need to load the old field 'stagedSessionId' as fallback.
+ dataJson.optInt("originalSessionId", dataJson.optInt("stagedSessionId", -1)),
rollbackStateFromString(dataJson.getString("state")),
dataJson.optString("stateDescription"),
dataJson.getBoolean("restoreUserDataInProgress"),
diff --git a/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java b/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
index a582914..82ba60f 100644
--- a/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
+++ b/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
@@ -53,17 +53,15 @@
private static final String TAG = RemoteRotationResolverService.class.getSimpleName();
private final long mIdleUnbindTimeoutMs;
- private final Object mLock;
RemoteRotationResolverService(Context context, ComponentName serviceName,
- int userId, long idleUnbindTimeoutMs, Object lock) {
+ int userId, long idleUnbindTimeoutMs) {
super(context,
new Intent(RotationResolverService.SERVICE_INTERFACE).setComponent(serviceName),
BIND_FOREGROUND_SERVICE | BIND_INCLUDE_CAPABILITIES, userId,
IRotationResolverService.Stub::asInterface);
mIdleUnbindTimeoutMs = idleUnbindTimeoutMs;
- mLock = lock;
// Bind right away.
connect();
@@ -75,8 +73,7 @@
return -1;
}
- @GuardedBy("mLock")
- public void resolveRotationLocked(RotationRequest request) {
+ public void resolveRotation(RotationRequest request) {
final RotationResolutionRequest remoteRequest = request.mRemoteRequest;
post(service -> service.resolveRotation(request.mIRotationResolverCallback, remoteRequest));
@@ -97,13 +94,15 @@
@NonNull
private final IRotationResolverCallback mIRotationResolverCallback;
@NonNull
- private ICancellationSignal mCancellation;
- @NonNull
private final CancellationSignal mCancellationSignalInternal;
@NonNull
final RotationResolverInternal.RotationResolverCallbackInternal
mCallbackInternal;
+ @NonNull
+ @GuardedBy("mLock")
+ private ICancellationSignal mCancellation;
+
@GuardedBy("mLock")
boolean mIsFulfilled;
@@ -111,17 +110,19 @@
final RotationResolutionRequest mRemoteRequest;
boolean mIsDispatched;
- private final Object mLock = new Object();
+ private final Object mLock;
private final long mRequestStartTimeMillis;
RotationRequest(
@NonNull RotationResolverInternal.RotationResolverCallbackInternal callbackInternal,
- RotationResolutionRequest request, @NonNull CancellationSignal cancellationSignal) {
+ RotationResolutionRequest request, @NonNull CancellationSignal cancellationSignal,
+ Object lock) {
mCallbackInternal = callbackInternal;
mRemoteRequest = request;
mIRotationResolverCallback = new RotationResolverCallback(this);
mCancellationSignalInternal = cancellationSignal;
mRequestStartTimeMillis = SystemClock.elapsedRealtime();
+ mLock = lock;
}
@@ -153,7 +154,7 @@
}
private static class RotationResolverCallback extends IRotationResolverCallback.Stub {
- private WeakReference<RotationRequest> mRequestWeakReference;
+ private final WeakReference<RotationRequest> mRequestWeakReference;
RotationResolverCallback(RotationRequest request) {
this.mRequestWeakReference = new WeakReference<>(request);
@@ -215,7 +216,6 @@
}
}
}
-
}
}
}
diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
index ccf096e..29f3d10 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
@@ -121,26 +121,26 @@
final RotationResolverInternal.RotationResolverCallbackInternal wrapper =
new RotationResolverInternal.RotationResolverCallbackInternal() {
- @Override
- public void onSuccess(int result) {
- synchronized (mLock) {
- mLatencyTracker
- .onActionEnd(ACTION_ROTATE_SCREEN_CAMERA_CHECK);
- }
- callbackInternal.onSuccess(result);
- }
+ @Override
+ public void onSuccess(int result) {
+ synchronized (mLock) {
+ mLatencyTracker
+ .onActionEnd(ACTION_ROTATE_SCREEN_CAMERA_CHECK);
+ }
+ callbackInternal.onSuccess(result);
+ }
- @Override
- public void onFailure(int error) {
- synchronized (mLock) {
- mLatencyTracker
- .onActionEnd(ACTION_ROTATE_SCREEN_CAMERA_CHECK);
- }
- callbackInternal.onFailure(error);
- }
- };
+ @Override
+ public void onFailure(int error) {
+ synchronized (mLock) {
+ mLatencyTracker
+ .onActionEnd(ACTION_ROTATE_SCREEN_CAMERA_CHECK);
+ }
+ callbackInternal.onFailure(error);
+ }
+ };
mCurrentRequest = new RemoteRotationResolverService.RotationRequest(wrapper,
- request, cancellationSignalInternal);
+ request, cancellationSignalInternal, mLock);
cancellationSignalInternal.setOnCancelListener(() -> {
synchronized (mLock) {
@@ -152,7 +152,7 @@
});
- mRemoteService.resolveRotationLocked(mCurrentRequest);
+ mRemoteService.resolveRotation(mCurrentRequest);
mCurrentRequest.mIsDispatched = true;
}
@@ -160,7 +160,7 @@
private void ensureRemoteServiceInitiated() {
if (mRemoteService == null) {
mRemoteService = new RemoteRotationResolverService(getContext(), mComponentName,
- getUserId(), CONNECTION_TTL_MILLIS, mLock);
+ getUserId(), CONNECTION_TTL_MILLIS);
}
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
index 6366280..e006b65 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
@@ -21,24 +21,23 @@
import android.hardware.audio.common.V2_0.Uuid;
import android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback;
import android.hardware.soundtrigger.V2_3.ISoundTriggerHw;
-import android.hardware.soundtrigger.V2_3.Properties;
+import android.media.soundtrigger.AudioCapabilities;
+import android.media.soundtrigger.ConfidenceLevel;
+import android.media.soundtrigger.ModelParameter;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.Phrase;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseRecognitionExtra;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionMode;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.SoundModelType;
import android.media.audio.common.AudioConfig;
import android.media.audio.common.AudioOffloadInfo;
-import android.media.soundtrigger_middleware.AudioCapabilities;
-import android.media.soundtrigger_middleware.ConfidenceLevel;
-import android.media.soundtrigger_middleware.ModelParameter;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.Phrase;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.RecognitionMode;
-import android.media.soundtrigger_middleware.RecognitionStatus;
-import android.media.soundtrigger_middleware.SoundModel;
-import android.media.soundtrigger_middleware.SoundModelType;
-import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
import android.os.HidlMemory;
import android.os.HidlMemoryUtil;
import android.os.ParcelFileDescriptor;
@@ -55,9 +54,9 @@
*/
class ConversionUtil {
static @NonNull
- SoundTriggerModuleProperties hidl2aidlProperties(
+ Properties hidl2aidlProperties(
@NonNull ISoundTriggerHw.Properties hidlProperties) {
- SoundTriggerModuleProperties aidlProperties = new SoundTriggerModuleProperties();
+ Properties aidlProperties = new Properties();
aidlProperties.implementor = hidlProperties.implementor;
aidlProperties.description = hidlProperties.description;
aidlProperties.version = hidlProperties.version;
@@ -75,9 +74,9 @@
return aidlProperties;
}
- static @NonNull SoundTriggerModuleProperties hidl2aidlProperties(
- @NonNull Properties hidlProperties) {
- SoundTriggerModuleProperties aidlProperties = hidl2aidlProperties(hidlProperties.base);
+ static @NonNull Properties hidl2aidlProperties(
+ @NonNull android.hardware.soundtrigger.V2_3.Properties hidlProperties) {
+ Properties aidlProperties = hidl2aidlProperties(hidlProperties.base);
aidlProperties.supportedModelArch = hidlProperties.supportedModelArch;
aidlProperties.audioCapabilities =
hidl2aidlAudioCapabilities(hidlProperties.audioCapabilities);
@@ -216,9 +215,11 @@
}
static @NonNull android.hardware.soundtrigger.V2_3.RecognitionConfig aidl2hidlRecognitionConfig(
- @NonNull RecognitionConfig aidlConfig) {
+ @NonNull RecognitionConfig aidlConfig, int deviceHandle, int ioHandle) {
android.hardware.soundtrigger.V2_3.RecognitionConfig hidlConfig =
new android.hardware.soundtrigger.V2_3.RecognitionConfig();
+ hidlConfig.base.header.captureDevice = deviceHandle;
+ hidlConfig.base.header.captureHandle = ioHandle;
hidlConfig.base.header.captureRequested = aidlConfig.captureRequested;
for (PhraseRecognitionExtra aidlPhraseExtra : aidlConfig.phraseRecognitionExtras) {
hidlConfig.base.header.phrases.add(aidl2hidlPhraseRecognitionExtra(aidlPhraseExtra));
@@ -299,8 +300,6 @@
aidlEvent.status = hidl2aidlRecognitionStatus(hidlEvent.status);
aidlEvent.type = hidl2aidlSoundModelType(hidlEvent.type);
aidlEvent.captureAvailable = hidlEvent.captureAvailable;
- // hidlEvent.captureSession is never a valid field.
- aidlEvent.captureSession = -1;
aidlEvent.captureDelayMs = hidlEvent.captureDelayMs;
aidlEvent.capturePreambleMs = hidlEvent.capturePreambleMs;
aidlEvent.triggerInData = hidlEvent.triggerInData;
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/DefaultHalFactory.java b/services/core/java/com/android/server/soundtrigger_middleware/DefaultHalFactory.java
new file mode 100644
index 0000000..2f2cb59
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/DefaultHalFactory.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+import android.hardware.soundtrigger.V2_0.ISoundTriggerHw;
+import android.os.HwBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * This is the basic implementation of HalFactory, which uses either the default STHAL or a mock.
+ *
+ * The choice of which HAL to use is as follows:
+ * - Get the (int) value of "debug.soundtrigger_middleware.use_mock_hal" sysprop, if it doesn't
+ * exist, assume 0.
+ * - If the value is 0, use the default HAL on the device. Connect to the latest-version "default"
+ * instance declared in the device manifest (either AIDL or HIDL).
+ * - If the value is 2, connect to a "mock" instance of the latest v2.x (HIDL).
+ * - If the value is 3, connect to a "mock" instance of soundtrigger3 (AIDL).
+ * - Otherwise, throw.
+ */
+class DefaultHalFactory implements HalFactory {
+ private static final String TAG = "SoundTriggerMiddlewareDefaultHalFactory";
+
+ private static final @NonNull ICaptureStateNotifier mCaptureStateNotifier =
+ new ExternalCaptureStateTracker();
+
+ private static final int USE_DEFAULT_HAL = 0;
+ private static final int USE_MOCK_HAL_V2 = 2;
+ private static final int USE_MOCK_HAL_V3 = 3;
+
+ @Override
+ public ISoundTriggerHal create() {
+ try {
+ int mockHal = SystemProperties.getInt("debug.soundtrigger_middleware.use_mock_hal",
+ USE_DEFAULT_HAL);
+ if (mockHal == USE_DEFAULT_HAL) {
+ // Use production HAL.
+
+ // Try soundtrigger3 (AIDL) first.
+ final String aidlServiceName =
+ android.hardware.soundtrigger3.ISoundTriggerHw.class.getCanonicalName()
+ + "/default";
+ if (ServiceManager.isDeclared(aidlServiceName)) {
+ Log.i(TAG, "Connecting to default soundtrigger3.ISoundTriggerHw");
+ return new SoundTriggerHw3Compat(ServiceManager.waitForService(aidlServiceName),
+ () -> {
+ // This property needs to be defined in an init.rc script and
+ // trigger a HAL reboot.
+ SystemProperties.set("sys.audio.restart.hal", "1");
+ });
+ }
+
+ // Fallback to soundtrigger-V2.x (HIDL).
+ Log.i(TAG, "Connecting to default soundtrigger-V2.x.ISoundTriggerHw");
+ ISoundTriggerHw driver = ISoundTriggerHw.getService(true);
+ return SoundTriggerHw2Compat.create(driver, () -> {
+ // This property needs to be defined in an init.rc script and
+ // trigger a HAL reboot.
+ SystemProperties.set("sys.audio.restart.hal", "1");
+ }, mCaptureStateNotifier);
+ } else if (mockHal == USE_MOCK_HAL_V2) {
+ // Use V2 mock.
+ Log.i(TAG, "Connecting to mock soundtrigger-V2.x.ISoundTriggerHw");
+ HwBinder.setTrebleTestingOverride(true);
+ try {
+ ISoundTriggerHw driver = ISoundTriggerHw.getService("mock", true);
+ return SoundTriggerHw2Compat.create(driver, () -> {
+ try {
+ driver.debug(null, new ArrayList<>(Arrays.asList("reboot")));
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to reboot mock HAL", e);
+ }
+ }, mCaptureStateNotifier);
+ } finally {
+ HwBinder.setTrebleTestingOverride(false);
+ }
+ } else if (mockHal == USE_MOCK_HAL_V3) {
+ // Use V3 mock.
+ final String aidlServiceName =
+ android.hardware.soundtrigger3.ISoundTriggerHw.class.getCanonicalName()
+ + "/mock";
+ Log.i(TAG, "Connecting to mock soundtrigger3.ISoundTriggerHw");
+ return new SoundTriggerHw3Compat(ServiceManager.waitForService(aidlServiceName),
+ () -> {
+ try {
+ ServiceManager.waitForService(aidlServiceName).shellCommand(null,
+ null, null, new String[]{"reboot"}, null, null);
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to reboot mock HAL", e);
+ }
+ });
+ } else {
+ throw new RuntimeException("Unknown HAL mock version: " + mockHal);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java b/services/core/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java
index 9404904..d195fbe 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java
@@ -16,42 +16,55 @@
package com.android.server.soundtrigger_middleware;
+import android.annotation.NonNull;
import android.util.Log;
+import java.util.LinkedList;
+import java.util.List;
import java.util.concurrent.Semaphore;
-import java.util.function.Consumer;
/**
* This is a never-give-up listener for sound trigger external capture state notifications, as
* published by the audio policy service.
*
* This class will constantly try to connect to the service over a background thread and tolerate
- * its death. The client will be notified by a single provided function that is called in a
- * synchronized manner.
- * For simplicity, there is currently no way to stop the tracker. This is possible to add if the
- * need ever arises.
+ * its death.
*/
-class ExternalCaptureStateTracker {
+class ExternalCaptureStateTracker implements ICaptureStateNotifier {
private static final String TAG = "CaptureStateTracker";
- /** Our client's listener. */
- private final Consumer<Boolean> mListener;
+
+ /** Our client's listeners. Also used as lock. */
+ private final List<Listener> mListeners = new LinkedList<>();
+
+ /** Conservatively, until notified otherwise, we assume capture is active. */
+ private boolean mCaptureActive = true;
+
/** This semaphore will get a permit every time we need to reconnect. */
private final Semaphore mNeedToConnect = new Semaphore(1);
/**
* Constructor. Will start a background thread to do the work.
- *
- * @param listener A client provided listener that will be called on state
- * changes. May be
- * called multiple consecutive times with the same value. Never
- * called
- * concurrently.
*/
- ExternalCaptureStateTracker(Consumer<Boolean> listener) {
- mListener = listener;
+ ExternalCaptureStateTracker() {
new Thread(this::run).start();
}
+
+ @Override
+ public boolean registerListener(@NonNull Listener listener) {
+ synchronized (mListeners) {
+ mListeners.add(listener);
+ return mCaptureActive;
+ }
+ }
+
+ @Override
+ public void unregisterListener(Listener listener) {
+ synchronized (mListeners) {
+ mListeners.remove(listener);
+ }
+ }
+
/**
* Routine for the background thread. Keeps trying to reconnect.
*/
@@ -74,7 +87,12 @@
*/
private void setCaptureState(boolean active) {
try {
- mListener.accept(active);
+ synchronized (mListeners) {
+ mCaptureActive = active;
+ for (Listener listener : mListeners) {
+ listener.onCaptureStateChange(active);
+ }
+ }
} catch (Exception e) {
Log.e(TAG, "Exception caught while setting capture state", e);
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/HalFactory.java b/services/core/java/com/android/server/soundtrigger_middleware/HalFactory.java
index b19e2ed..6da8a79 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/HalFactory.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/HalFactory.java
@@ -16,16 +16,14 @@
package com.android.server.soundtrigger_middleware;
-import android.hardware.soundtrigger.V2_0.ISoundTriggerHw;
-
/**
- * A factory for creating instances of {@link ISoundTriggerHw}.
+ * A factory for creating instances of {@link ISoundTriggerHal}.
*
* @hide
*/
public interface HalFactory {
/**
- * @return An instance of {@link ISoundTriggerHw}.
+ * @return An instance of {@link ISoundTriggerHal}.
*/
- ISoundTriggerHw create();
+ ISoundTriggerHal create();
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ICaptureStateNotifier.java b/services/core/java/com/android/server/soundtrigger_middleware/ICaptureStateNotifier.java
new file mode 100644
index 0000000..07d83ca
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ICaptureStateNotifier.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+
+/**
+ * Allow registering listeners for tracking changes in audio capture state (when recording starts /
+ * stops). The client will be notified in a synchronized manner.
+ */
+interface ICaptureStateNotifier {
+ interface Listener {
+ void onCaptureStateChange(boolean state);
+ }
+
+ /**
+ * Register a listener for state change notifications. Returns the current capture state and
+ * any subsequent changes will be sent to the listener.
+ * @param listener The listener.
+ * @return The state at the time of registration.
+ */
+ boolean registerListener(@NonNull Listener listener);
+
+ /**
+ * Unregister a listener, previously registered with {@link #registerListener(Listener)}.
+ * Once this call returns, no more invocations of the listener will be made.
+ */
+ void unregisterListener(@NonNull Listener listener);
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHal.java b/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHal.java
new file mode 100644
index 0000000..aa85dd0
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHal.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.soundtrigger_middleware;
+
+import android.hardware.soundtrigger3.ISoundTriggerHw;
+import android.hardware.soundtrigger3.ISoundTriggerHwCallback;
+import android.hardware.soundtrigger3.ISoundTriggerHwGlobalCallback;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.SoundModel;
+import android.os.IBinder;
+
+/**
+ * This interface mimics the soundtrigger HAL interface, with a few key differences:
+ * <ul>
+ * <li>Generally, methods should not throw, except for the following cases:
+ * <ul>
+ * <li>Any unexpected HAL behavior is considered an internal HAL malfunction and should be thrown
+ * as a {@link HalException}, from any method.
+ * <li>A {@link RuntimeException} with a {@link android.os.DeadObjectException} cause represents
+ * a dead HAL process and may be thrown by any method.
+ * <li>Implementations of earlier versions of the interface may throw a
+ * {@link RecoverableException} with a
+ * {@link android.media.soundtrigger.Status#OPERATION_NOT_SUPPORTED} for methods that
+ * have been introduced in later versions of the interface.
+ * <li>Certain methods are allowed to throw a {@link RecoverableException} with a
+ * {@link android.media.soundtrigger.Status#RESOURCE_CONTENTION} to indicate transient
+ * failures.
+ * </ul>
+ * <li>Some binder-specific details are hidden.
+ * <li>No RemoteExceptions are specified. Some implementations of this interface may rethrow
+ * RemoteExceptions as RuntimeExceptions, some can guarantee handling them somehow and never throw
+ * them.
+ * </ul>
+ * For cases where the client wants to explicitly handle specific versions of the underlying driver
+ * interface, they may call {@link #interfaceDescriptor()}.
+ * <p>
+ * <b>Note to maintainers</b>: This class must always be kept in sync with the latest version,
+ * so that clients have access to the entire functionality without having to burden themselves with
+ * compatibility, as much as possible.
+ */
+interface ISoundTriggerHal {
+ /**
+ * @see ISoundTriggerHw#getProperties()
+ */
+ Properties getProperties();
+
+ /**
+ * @see ISoundTriggerHw#registerGlobalCallback(ISoundTriggerHwGlobalCallback)
+ */
+ void registerCallback(GlobalCallback callback);
+
+ /**
+ * @see ISoundTriggerHw#loadSoundModel(android.media.soundtrigger.SoundModel,
+ * ISoundTriggerHwCallback)
+ */
+ int loadSoundModel(SoundModel soundModel, ModelCallback callback);
+
+ /**
+ * @see ISoundTriggerHw#loadPhraseSoundModel(android.media.soundtrigger.PhraseSoundModel,
+ * ISoundTriggerHwCallback)
+ */
+ int loadPhraseSoundModel(PhraseSoundModel soundModel, ModelCallback callback);
+
+ /**
+ * @see ISoundTriggerHw#unloadSoundModel(int)
+ */
+ void unloadSoundModel(int modelHandle);
+
+ /**
+ * @see ISoundTriggerHw#startRecognition(int, int, int, RecognitionConfig)
+ */
+ void startRecognition(int modelHandle, int deviceHandle, int ioHandle,
+ RecognitionConfig config);
+
+ /**
+ * @see ISoundTriggerHw#stopRecognition(int)
+ */
+ void stopRecognition(int modelHandle);
+
+ /**
+ * @see ISoundTriggerHw#forceRecognitionEvent(int)
+ */
+ void forceRecognitionEvent(int modelHandle);
+
+ /**
+ * @return null if not supported.
+ * @see ISoundTriggerHw#queryParameter(int, int)
+ */
+ ModelParameterRange queryParameter(int modelHandle, int param);
+
+ /**
+ * @see ISoundTriggerHw#getParameter(int, int)
+ */
+ int getModelParameter(int modelHandle, int param);
+
+ /**
+ * @see ISoundTriggerHw#setParameter(int, int, int)
+ */
+ void setModelParameter(int modelHandle, int param, int value);
+
+ /**
+ * @see IBinder#getInterfaceDescriptor()
+ */
+ String interfaceDescriptor();
+
+ /**
+ * @see IBinder#linkToDeath(IBinder.DeathRecipient, int)
+ */
+ void linkToDeath(IBinder.DeathRecipient recipient);
+
+ /**
+ * @see IBinder#unlinkToDeath(IBinder.DeathRecipient, int)
+ */
+ void unlinkToDeath(IBinder.DeathRecipient recipient);
+
+ /*
+ * This is only useful for testing decorators and doesn't actually do anything with the real
+ * HAL. This method would block until all callbacks that were previously generated have been
+ * invoked. For most decorators, this merely flushes the delegate, but for delegates that may
+ * have additional buffers for callbacks this should flush them.
+ */
+ void flushCallbacks();
+
+ /**
+ * Kill and restart the HAL instance. This is typically a last resort for error recovery and may
+ * result in other related services being killed.
+ */
+ void reboot();
+
+ /**
+ * Called when this interface is guaranteed to no longer be used and can free up any resources
+ * used.
+ */
+ void detach();
+
+ /**
+ * Callback interface for model-related events.
+ */
+ interface ModelCallback {
+ /**
+ * @see ISoundTriggerHwCallback#recognitionCallback(int, RecognitionEvent)
+ */
+ void recognitionCallback(int modelHandle, RecognitionEvent event);
+
+ /**
+ * @see ISoundTriggerHwCallback#phraseRecognitionCallback(int, PhraseRecognitionEvent)
+ */
+ void phraseRecognitionCallback(int modelHandle, PhraseRecognitionEvent event);
+
+ /**
+ * @see ISoundTriggerHwCallback#modelUnloaded(int)
+ */
+ void modelUnloaded(int modelHandle);
+ }
+
+ /**
+ * Callback interface for global events.
+ */
+ interface GlobalCallback {
+ /**
+ * @see ISoundTriggerHwGlobalCallback#onResourcesAvailable()
+ */
+ void onResourcesAvailable();
+ }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHw2.java b/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHw2.java
deleted file mode 100644
index 8b434bd..0000000
--- a/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHw2.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.soundtrigger_middleware;
-
-import android.hardware.soundtrigger.V2_3.ModelParameterRange;
-import android.hidl.base.V1_0.IBase;
-import android.os.IHwBinder;
-
-/**
- * This interface mimics android.hardware.soundtrigger.V2_x.ISoundTriggerHw and
- * android.hardware.soundtrigger.V2_x.ISoundTriggerHwCallback, with a few key differences:
- * <ul>
- * <li>Methods in the original interface generally have a status return value and potentially a
- * second return value which is the actual return value. This is reflected via a synchronous
- * callback, which is not very pleasant to work with. This interface replaces that pattern with
- * the convention that a HalException is thrown for non-OK status, and then we can use the
- * return value for the actual return value.
- * <li>This interface will always include all the methods from the latest 2.x version (and thus
- * from every 2.x version) interface, with the convention that unsupported methods throw a
- * {@link RecoverableException} with a
- * {@link android.media.soundtrigger_middleware.Status#OPERATION_NOT_SUPPORTED}
- * code.
- * <li>Cases where the original interface had multiple versions of a method representing the exact
- * thing, or there exists a trivial conversion between the new and old version, this interface
- * represents only the latest version, without any _version suffixes.
- * <li>Removes some of the obscure IBinder methods.
- * <li>No RemoteExceptions are specified. Some implementations of this interface may rethrow
- * RemoteExceptions as RuntimeExceptions, some can guarantee handling them somehow and never throw
- * them.
- * <li>soundModelCallback has been removed, since nobody cares about it. Implementations are free
- * to silently discard it.
- * </ul>
- * For cases where the client wants to explicitly handle specific versions of the underlying driver
- * interface, they may call {@link #interfaceDescriptor()}.
- * <p>
- * <b>Note to maintainers</b>: This class must always be kept in sync with the latest 2.x version,
- * so that clients have access to the entire functionality without having to burden themselves with
- * compatibility, as much as possible.
- */
-public interface ISoundTriggerHw2 {
- /**
- * @see android.hardware.soundtrigger.V2_3.ISoundTriggerHw#getPropertiesEx(
- * android.hardware.soundtrigger.V2_3.ISoundTriggerHw.getPropertiesExCallback)
- */
- android.hardware.soundtrigger.V2_3.Properties getProperties();
-
- /**
- * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#loadSoundModel_2_1(android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel,
- * android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback, int,
- * android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadSoundModel_2_1Callback)
- */
- int loadSoundModel(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel soundModel,
- SoundTriggerHw2Compat.Callback callback, int cookie);
-
- /**
- * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#loadPhraseSoundModel_2_1(android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel,
- * android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback, int,
- * android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadPhraseSoundModel_2_1Callback)
- */
- int loadPhraseSoundModel(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel soundModel,
- SoundTriggerHw2Compat.Callback callback, int cookie);
-
- /**
- * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#unloadSoundModel(int)
- */
- void unloadSoundModel(int modelHandle);
-
- /**
- * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#stopRecognition(int)
- */
- void stopRecognition(int modelHandle);
-
- /**
- * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#stopAllRecognitions()
- */
- void stopAllRecognitions();
-
- /**
- * @see android.hardware.soundtrigger.V2_3.ISoundTriggerHw#startRecognition_2_3(int,
- * android.hardware.soundtrigger.V2_3.RecognitionConfig,
- * android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback, int)
- */
- void startRecognition(int modelHandle,
- android.hardware.soundtrigger.V2_3.RecognitionConfig config,
- SoundTriggerHw2Compat.Callback callback, int cookie);
-
- /**
- * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#getModelState(int)
- */
- void getModelState(int modelHandle);
-
- /**
- * @see android.hardware.soundtrigger.V2_3.ISoundTriggerHw#getParameter(int, int,
- * ISoundTriggerHw.getParameterCallback)
- */
- int getModelParameter(int modelHandle, int param);
-
- /**
- * @see android.hardware.soundtrigger.V2_3.ISoundTriggerHw#setParameter(int, int, int)
- */
- void setModelParameter(int modelHandle, int param, int value);
-
- /**
- * @return null if not supported.
- * @see android.hardware.soundtrigger.V2_3.ISoundTriggerHw#queryParameter(int, int,
- * ISoundTriggerHw.queryParameterCallback)
- */
- ModelParameterRange queryParameter(int modelHandle, int param);
-
- /**
- * @see IHwBinder#linkToDeath(IHwBinder.DeathRecipient, long)
- */
- boolean linkToDeath(IHwBinder.DeathRecipient recipient, long cookie);
-
- /**
- * @see IHwBinder#unlinkToDeath(IHwBinder.DeathRecipient)
- */
- boolean unlinkToDeath(IHwBinder.DeathRecipient recipient);
-
- /**
- * @see IBase#interfaceDescriptor()
- */
- String interfaceDescriptor() throws android.os.RemoteException;
-
- interface Callback {
- /**
- * @see android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback#recognitionCallback_2_1(android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent,
- * int)
- */
- void recognitionCallback(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent event,
- int cookie);
-
- /**
- * @see android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback#phraseRecognitionCallback_2_1(android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent,
- * int)
- */
- void phraseRecognitionCallback(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent event,
- int cookie);
- }
-}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerMiddlewareInternal.java b/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerMiddlewareInternal.java
index a90053a..60f89da 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerMiddlewareInternal.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerMiddlewareInternal.java
@@ -16,18 +16,18 @@
package com.android.server.soundtrigger_middleware;
-import android.media.ICaptureStateListener;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
/**
- * This interface unifies methods from ISoundTriggerMiddlewareService and ICaptureStateListener.
+ * This interface closely follows ISoundTriggerMiddlewareService with some subtle changes for
+ * convenience.
*
* The ISoundTriggerMiddlewareService have been modified to exclude identity information and the
* RemoteException signature, both of which are only relevant at the service boundary layer.
*/
-public interface ISoundTriggerMiddlewareInternal extends ICaptureStateListener {
+public interface ISoundTriggerMiddlewareInternal {
/**
* Query the available modules and their capabilities.
*/
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/README.md b/services/core/java/com/android/server/soundtrigger_middleware/README.md
index 416548d..016e5c9 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/README.md
+++ b/services/core/java/com/android/server/soundtrigger_middleware/README.md
@@ -1,19 +1,145 @@
# Sound Trigger Middleware
-TODO: Add component description.
-## Notes about thread synchronization
+## Overview
+Sound Trigger Middleware is a system service that exposes sound trigger functionality (low-power
+detection of acoustic events) to applications and higher-level system service.
+
+It has the following roles:
+- Isolating the soundtrigger HAL from potentially untrusted clients.
+- Enforcing correct behavior of the clients.
+- Enforcing correct behavior of the HAL and attempting to recover from failures.
+- Enforcing permissions for using soundtrigger functionality.
+- Serializing access to the HAL.
+- Logging soundtrigger usage in a comprehensive and consistent manner.
+- Generating a dumpsys report including current state and history of operations.
+- Providing a standard interface regardless which version of the HAL is implemented and gracefully
+ degrading operation whenever necessary.
+
+## Structure
+
+The service implementation can be divided into three main layers:
+
+- The "bottom layer" is concerned with HAL compatibility - making all HAL versions look and behave
+ the same.
+- The "middle layer" is concerned with the business logic of the service.
+- The "top layer" is concerned with exposing this functionality as a System Service and integrating
+ with other parts of the system.
+
+### HAL Compatibility Layer
+
+This layer implements the `ISoundTriggerHal` interface, which is the version-agnostic representation
+of the sound trigger HAL driver. It has two main implementations, `SoundTriggerHw2Compat` and
+`SoundTriggerHw3Compat` responsible for adapting to V2.x and V3 HAL drivers, respectively, including
+supporting their respective minor-version differences.
+
+This layer also includes several `ISoundTriggerHal` decorators, such as `SoundTriggerHalWatchdog`
+that enforces deadlines on calls into the HAL, and `SoundTriggerHalEnforcer` which enforces that
+the HAL respects the expected protocol.
+
+The decorator-based design is an effective tool for separation of aspects and modularity, thus
+keeping classes relatively small and focused on one concern. It is also very effective for
+testability by following dependency injection principles.
+
+### Business Logic Layer
+
+This layer also uses a decorator-based design for separation of concerns. The main interface being
+decorated is `ISoundTriggerMiddlwareInternal`, which closely follows the external-facing AIDL
+interface, `ISoundTriggerMiddlewareService`.
+
+Each of the decorators serves a focused purpose: for example, `SoundTriggerMiddlwarePermission`
+deals with enforcing permissions required for the various methods, `SoundTriggerMiddlewareLogging`
+logs all API usage, `SoundTriggerMiddlewareValidation` enforces correct usage of the protocol and
+isolates client errors from internal server errors.
+
+At the bottom of this decorator stack is `SoundTriggerMiddlewareImpl` / `SoundTriggerModule`, which
+are the adapter between `ISoundTriggerHal` and `ISoundTriggerMiddlwareInternal`, introducing the
+notion of having separate client sessions sharing the same HAL.
+
+### Service Layer
+
+This layer ties everything together. It instantiates the actual system service and the decorator
+stack. It also provides concrete connections to the Audio service (for negotiating sessions shared
+between Audio and Sound Trigger and for notifications about audio recording) and to the various HAL
+factories.
+
+This is the only layer that makes strong assumptions about the environment instead of relying on
+abstractions.
+
+## Error Handling and Exception Conventions
+
+We follow conventions for usage of exceptions in the service, in order to correctly and consistently
+distinguish the following cases:
+
+1. The client has done something wrong.
+2. The service implementation has done something wrong.
+3. The HAL has done something wrong.
+4. Nobody has done anything wrong, but runtime conditions prevent an operation from being fulfilled
+ as intended.
+
+The `SoundTriggerMiddlewarePermission` class would reject any calls from unauthorized clients,
+responding with the appropriate exception.
+
+The `SoundTriggerMiddlewareValidation` class does much of this separation. By validating the
+client's data and state, it would throw a relevant `RuntimeException` exception to the client
+without passing the requests down to the lower layers. Once that is done, any exception thrown from
+the underlying implementation can be assumed to be not the client's fault. If caught, they will be
+classified according to the following rule:
+
+- If they are `RecoverableException`s, they represent category #4 above, and will be presented to
+ the client as `ServiceSpecificException`s with the same error code.
+- Otherwise, they are considered an internal error (including HAL malfunction) and will be
+ presented to the client as `ServiceSpecificException(Status.INTERNAL_ERROR)`.
+
+Internally, we would throw `RecoverableException` whenever appropriate. Whenever a HAL malfunctions,
+`SoundTriggerHalEnforcer` is responsible for rebooting it and throwing an exception. A HAL death is
+considered a valid failure mode, and thus result in `RecoverableException(Status.DEAD_OBJECT)`,
+which ends up as a `ServiceSpecificException(Status.DEAD_OBJECT)` on the client side.
+
+## Notes About Thread Synchronization
This component has some tricky thread synchronization considerations due to its layered design and
due to the fact that it is involved in both in-bound and out-bound calls from / to
-external components. To avoid potential deadlocks, a strict locking order must be ensured whenever
-nesting locks. The order is:
-- `SoundTriggerMiddlewareValidation` lock.
-- Audio policy service lock. This one is external - it should be assumed to be held whenever we're
- inside the `ExternalCaptureStateTracker.setCaptureState()` call stack *AND* to be acquired from
- within our calls into `AudioSessionProvider.acquireSession()`.
-- `SoundTriggerModule` lock.
+external components.
-This dictates careful consideration of callbacks going from `SoundTriggerModule` to
-`SoundTriggerMiddlewareValidation` and especially those coming from the `setCaptureState()` path.
-We always invoke those calls outside of the `SoundTriggerModule` lock, so we can lock
-`SoundTriggerMiddlewareValidation`. However, in the `setCaptureState()` case, we have to use atomics
-in `SoundTriggerMiddlewareValidation` and avoid the lock.
+The following mutexes need to be considered:
+- Typically, a one or more mutexes that exist in every layer of the sound trigger middleware stack
+ to serialize access to its internal state or to external components.
+- Audio Policy Service lock. This one is external - it should be assumed to be held whenever we're
+ inside the `ExternalCaptureStateTracker.setCaptureState()` call stack *AND* to be acquired from
+ within our calls into `AudioSessionProvider.acquireSession()` /
+ `AudioSessionProvider.releaseSession()`.
+
+To avoid potential deadlocks, a strict locking order must be ensured whenever nesting locks. The
+order is:
+- Upper layers of the stack, starting from the top (i.e. may not attempt to acquire a higher-layer
+ mutex while a lower-layer mutex is being held) until `ISoundTriggerHw2`.
+- Audio Policy Service lock.
+- Lower layers of the stack, starting from `ISoundTriggerHw2` all the way down to the HAL.
+
+In order to enforce this order, some conventions are established around when it is safe for a module
+to call another module, while having its local mutex(es) held:
+- Most calls (see exceptions below) originating from SoundTriggerMiddlewareService simply propagate
+ down the decorator stack. It is legal to call into the next layer down while holding a local
+ mutex. It is illegal to invoke a callback with a local mutex held.
+- Callbacks propagate from the lower layers up to the upper layers. It is legal to hold a local
+ mutex within a callback, but **not** while call to an upper layer.
+- In order to be able to synchronize, despite the asynchronous nature of callbacks,
+ `stopRecognition()` and `unloadModel()` work differently. They guarantee that once they return,
+ the callbacks associated with them will no longer be called. This implies that they have to block
+ until any pending callbacks are done processing and since these callbacks are potentially holding
+ locks of higher-order mutexes, we must not be holding a local mutex while calling down. The proper
+ sequence for these calls is:
+ - Obtain the local lock if needed. Update/check local state as necessary.
+ - Call the respective method of the delegate ("downwards"). Once it returns, not more callbacks
+ related to this operation will be called.
+ - Obtain the local lock if needed. Update local state as necessary. Assume that state might have
+ changed while the lock has been released.
+ - Release the local lock.
+ - Invoke any synchronous callbacks if needed.
+- Calling from `SoundTriggerMiddlewareImpl` / `SoundTriggerModule` into the audio policy service via
+ `acquireSession()` / `releaseSession()` while holding the local lock is legal.
+- `setCaptureState()` calls, originating from Audio Policy Service, into the lower layers of the
+ stack may call into the HAL (specifically, they must invoke `stopRecognition()`, but must not
+ block on callbacks. For this reason, `SoundTriggerHw2ConcurrentCaptureHandler`, which is the
+ recipient of these calls, features a buffer and an additional thread, which allows the actual
+ stopping to be synchronous, as required, without having to block the call upon higher layers
+ processing the callbacks.
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java
new file mode 100644
index 0000000..e3ce719
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java
@@ -0,0 +1,456 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+import android.media.permission.SafeCloseable;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.SoundModelType;
+import android.media.soundtrigger.Status;
+import android.os.IBinder;
+
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * This is a decorator around ISoundTriggerHal, which implements enforcement of concurrent capture
+ * constraints, for HAL implementations older than V2.4 (later versions support this feature at the
+ * HAL level).
+ * <p>
+ * Decorating an instance with this class would result in all active recognitions being aborted as
+ * soon as capture state becomes active. This class ensures consistent handling of abortions coming
+ * from that HAL and abortions coming from concurrent capture, in that only one abort event will be
+ * delivered, irrespective of the relative timing of the two events.
+ * <p>
+ * There are some delicate thread-safety issues handled here:
+ * <ul>
+ * <li>When a model is stopped via stopRecognition(), we guarantee that by the time the call
+ * returns, there will be no more recognition events (including abort) delivered for this model.
+ * This implies synchronous stopping and blocking until all pending events have been delivered.
+ * <li>When a model is stopped via onCaptureStateChange(true), the stopping of the recognition at
+ * the HAL level must be synchronous, but the call must not block on the delivery of the
+ * callbacks, due to the risk of a deadlock: the onCaptureStateChange() calls are typically
+ * invoked with the audio policy mutex held, so must not call method which may attempt to lock
+ * higher-level mutexes. See README.md in this directory for further details.
+ * </ul>
+ * The way this behavior is achieved is by having an additional thread with an event queue, which
+ * joins together model events coming from the delegate module with abort events originating from
+ * this layer (as result of external capture).
+ */
+public class SoundTriggerHalConcurrentCaptureHandler implements ISoundTriggerHal,
+ ICaptureStateNotifier.Listener {
+ private final @NonNull ISoundTriggerHal mDelegate;
+ private GlobalCallback mGlobalCallback;
+
+ /**
+ * Information about a model that is currently loaded. This is needed in order to be able to
+ * send abort events to its designated callback.
+ */
+ private static class LoadedModel {
+ final int type;
+ final @NonNull ModelCallback callback;
+
+ private LoadedModel(int type, @NonNull ModelCallback callback) {
+ this.type = type;
+ this.callback = callback;
+ }
+ }
+
+ /**
+ * This map holds the model type for every model that is loaded.
+ */
+ private final @NonNull Map<Integer, LoadedModel> mLoadedModels = new ConcurrentHashMap<>();
+
+ /**
+ * A set of all models that are currently active.
+ * We use this in order to know which models to stop in case of external capture.
+ * Used as a lock to synchronize operations that effect activity.
+ */
+ private final @NonNull Set<Integer> mActiveModels = new HashSet<>();
+
+ /**
+ * Notifier for changes in capture state.
+ */
+ private final @NonNull ICaptureStateNotifier mNotifier;
+
+ /**
+ * Whether capture is active.
+ */
+ private boolean mCaptureState;
+
+ /**
+ * Since we're wrapping the death recipient, we need to keep a translation map for unlinking.
+ * Key is the client recipient, value is the wrapper.
+ */
+ private final @NonNull Map<IBinder.DeathRecipient, IBinder.DeathRecipient>
+ mDeathRecipientMap = new ConcurrentHashMap<>();
+
+ private final @NonNull CallbackThread mCallbackThread = new CallbackThread();
+
+ public SoundTriggerHalConcurrentCaptureHandler(
+ @NonNull ISoundTriggerHal delegate,
+ @NonNull ICaptureStateNotifier notifier) {
+ mDelegate = delegate;
+ mNotifier = notifier;
+ mCaptureState = mNotifier.registerListener(this);
+ }
+
+ @Override
+ public void startRecognition(int modelHandle, int deviceHandle, int ioHandle,
+ RecognitionConfig config) {
+ synchronized (mActiveModels) {
+ if (mCaptureState) {
+ throw new RecoverableException(Status.RESOURCE_CONTENTION);
+ }
+ mDelegate.startRecognition(modelHandle, deviceHandle, ioHandle, config);
+ mActiveModels.add(modelHandle);
+ }
+ }
+
+ @Override
+ public void stopRecognition(int modelHandle) {
+ synchronized (mActiveModels) {
+ mDelegate.stopRecognition(modelHandle);
+ mActiveModels.remove(modelHandle);
+ }
+ // Block until all previous events are delivered. Since this is potentially blocking on
+ // upward calls, it must be done outside the lock.
+ mCallbackThread.flush();
+ }
+
+ @Override
+ public void onCaptureStateChange(boolean active) {
+ synchronized (mActiveModels) {
+ if (active) {
+ // Abort all active models. This must be done as one transaction to the event
+ // thread, in order to be able to dedupe events before they are delivered.
+ try (SafeCloseable ignored = mCallbackThread.stallReader()) {
+ for (int modelHandle : mActiveModels) {
+ mDelegate.stopRecognition(modelHandle);
+ LoadedModel model = mLoadedModels.get(modelHandle);
+ // An abort event must be the last one for its model.
+ mCallbackThread.pushWithDedupe(modelHandle, true,
+ () -> notifyAbort(modelHandle, model));
+ }
+ }
+ } else {
+ mGlobalCallback.onResourcesAvailable();
+ }
+
+ mCaptureState = active;
+ }
+ }
+
+ @Override
+ public int loadSoundModel(SoundModel soundModel, ModelCallback callback) {
+ int handle = mDelegate.loadSoundModel(soundModel, new CallbackWrapper(callback));
+ mLoadedModels.put(handle, new LoadedModel(SoundModelType.GENERIC, callback));
+ return handle;
+ }
+
+ @Override
+ public int loadPhraseSoundModel(PhraseSoundModel soundModel,
+ ModelCallback callback) {
+ int handle = mDelegate.loadPhraseSoundModel(soundModel, new CallbackWrapper(callback));
+ mLoadedModels.put(handle, new LoadedModel(SoundModelType.KEYPHRASE, callback));
+ return handle;
+ }
+
+ @Override
+ public void unloadSoundModel(int modelHandle) {
+ mLoadedModels.remove(modelHandle);
+ mDelegate.unloadSoundModel(modelHandle);
+ }
+
+ @Override
+ public void registerCallback(GlobalCallback callback) {
+ mGlobalCallback = new GlobalCallback() {
+ @Override
+ public void onResourcesAvailable() {
+ mCallbackThread.push(callback::onResourcesAvailable);
+ }
+ };
+ mDelegate.registerCallback(mGlobalCallback);
+ }
+
+ @Override
+ public void linkToDeath(IBinder.DeathRecipient recipient) {
+ IBinder.DeathRecipient wrapper = new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ mCallbackThread.push(() -> recipient.binderDied());
+ }
+ };
+ mDelegate.linkToDeath(wrapper);
+ mDeathRecipientMap.put(recipient, wrapper);
+ }
+
+ @Override
+ public void unlinkToDeath(IBinder.DeathRecipient recipient) {
+ mDelegate.unlinkToDeath(mDeathRecipientMap.remove(recipient));
+ }
+
+ private class CallbackWrapper implements ISoundTriggerHal.ModelCallback {
+ private final @NonNull ISoundTriggerHal.ModelCallback mDelegateCallback;
+
+ private CallbackWrapper(@NonNull ModelCallback delegateCallback) {
+ mDelegateCallback = delegateCallback;
+ }
+
+ @Override
+ public void recognitionCallback(int modelHandle, RecognitionEvent event) {
+ // A recognition event must be the last one for its model, unless it is a forced one
+ // (those leave the model active).
+ mCallbackThread.pushWithDedupe(modelHandle,
+ event.status != RecognitionStatus.FORCED,
+ () -> mDelegateCallback.recognitionCallback(modelHandle, event));
+ }
+
+ @Override
+ public void phraseRecognitionCallback(int modelHandle, PhraseRecognitionEvent event) {
+ // A recognition event must be the last one for its model, unless it is a forced one
+ // (those leave the model active).
+ mCallbackThread.pushWithDedupe(modelHandle,
+ event.common.status != RecognitionStatus.FORCED,
+ () -> mDelegateCallback.phraseRecognitionCallback(modelHandle, event));
+ }
+
+ @Override
+ public void modelUnloaded(int modelHandle) {
+ mCallbackThread.push(() -> mDelegateCallback.modelUnloaded(modelHandle));
+ }
+ }
+
+ @Override
+ public void flushCallbacks() {
+ mDelegate.flushCallbacks();
+ mCallbackThread.flush();
+ }
+
+ /**
+ * This is a thread for asynchronous delivery of callback events, having the following features:
+ * <ul>
+ * <li>Events are processed on a separate thread than the thread that pushed them, in the order
+ * they were pushed.
+ * <li>Events can be deduped upon entry to the queue. This is achieved as follows:
+ * <ul>
+ * <li>Temporarily stall the reader via {@link #stallReader()}.
+ * <li>Within this scope, push as many events as needed via
+ * {@link #pushWithDedupe(int, boolean, Runnable)}.
+ * If an event with the same model handle as the one being pushed is already in the queue
+ * and has been marked as "lastForModel", the new event will be discarded before entering
+ * the queue.
+ * <li>Finally, un-stall the reader by existing the scope.
+ * <li>Events that do not require deduping can be pushed via {@link #push(Runnable)}.
+ * </ul>
+ * <li>Events can be flushed via {@link #flush()}. This will block until all events pushed prior
+ * to this call have been fully processed.
+ * </ul>
+ */
+ private static class CallbackThread {
+ private static class Entry {
+ final boolean lastForModel;
+ final int modelHandle;
+ final Runnable runnable;
+
+ private Entry(boolean lastForModel, int modelHandle, Runnable runnable) {
+ this.lastForModel = lastForModel;
+ this.modelHandle = modelHandle;
+ this.runnable = runnable;
+ }
+ }
+
+ private boolean mStallReader = false;
+ private final Queue<Entry> mList = new LinkedList<>();
+ private int mPushCount = 0;
+ private int mProcessedCount = 0;
+
+ /**
+ * Ctor. Starts the thread.
+ */
+ CallbackThread() {
+ new Thread(() -> {
+ try {
+ while (true) {
+ pop().run();
+ synchronized (mList) {
+ mProcessedCount++;
+ mList.notifyAll();
+ }
+ }
+ } catch (InterruptedException e) {
+ // If interrupted, exit.
+ }
+ }).start();
+ }
+
+ /**
+ * Push a new runnable to the queue, with no deduping.
+ *
+ * @param runnable The runnable to push.
+ */
+ void push(Runnable runnable) {
+ pushEntry(new Entry(false, 0, runnable), false);
+ }
+
+
+ /**
+ * Push a new runnable to the queue, with deduping.
+ * If an entry with the same model handle is already in the queue and was designated as
+ * last for model, this one will be discarded.
+ *
+ * @param modelHandle The model handle, used for deduping purposes.
+ * @param lastForModel If true, this entry will be considered the last one for this model
+ * and any subsequence calls for this handle (whether lastForModel or
+ * not) will be discarded while this entry is in the queue.
+ * @param runnable The runnable to push.
+ */
+ void pushWithDedupe(int modelHandle, boolean lastForModel, Runnable runnable) {
+ pushEntry(new Entry(lastForModel, modelHandle, runnable), true);
+ }
+
+ /**
+ * Block until every entry pushed prior to this call has been processed.
+ */
+ void flush() {
+ try {
+ synchronized (mList) {
+ int pushCount = mPushCount;
+ while (mProcessedCount != pushCount) {
+ mList.wait();
+ }
+ }
+ } catch (InterruptedException ignored) {
+ }
+ }
+
+ /**
+ * Creates a scope (using a try-with-resources block), within which events that are pushed
+ * remain queued and processed. This is useful in order to utilize deduping.
+ */
+ SafeCloseable stallReader() {
+ synchronized (mList) {
+ mStallReader = true;
+ return () -> {
+ synchronized (mList) {
+ mStallReader = false;
+ mList.notifyAll();
+ }
+ };
+ }
+ }
+
+ private void pushEntry(Entry entry, boolean dedupe) {
+ synchronized (mList) {
+ if (dedupe) {
+ for (Entry existing : mList) {
+ if (existing.lastForModel && existing.modelHandle == entry.modelHandle) {
+ return;
+ }
+ }
+ }
+ mList.add(entry);
+ mPushCount++;
+ mList.notifyAll();
+ }
+ }
+
+ private Runnable pop() throws InterruptedException {
+ synchronized (mList) {
+ while (mStallReader || mList.isEmpty()) {
+ mList.wait();
+ }
+ return mList.remove().runnable;
+ }
+ }
+ }
+
+ /** Notify the client that recognition has been aborted. */
+ private static void notifyAbort(int modelHandle, LoadedModel model) {
+ switch (model.type) {
+ case SoundModelType.GENERIC: {
+ RecognitionEvent event = new RecognitionEvent();
+ event.status = RecognitionStatus.ABORTED;
+ event.type = SoundModelType.GENERIC;
+ model.callback.recognitionCallback(modelHandle, event);
+ }
+ break;
+
+ case SoundModelType.KEYPHRASE: {
+ PhraseRecognitionEvent event = new PhraseRecognitionEvent();
+ event.common.status = RecognitionStatus.ABORTED;
+ event.common.type = SoundModelType.KEYPHRASE;
+ model.callback.phraseRecognitionCallback(modelHandle, event);
+ }
+ break;
+ }
+ }
+
+ @Override
+ public void detach() {
+ mDelegate.detach();
+ mNotifier.unregisterListener(this);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ // All methods below do trivial delegation - no interesting logic.
+ @Override
+ public void reboot() {
+ mDelegate.reboot();
+ }
+
+ @Override
+ public Properties getProperties() {
+ return mDelegate.getProperties();
+ }
+
+ @Override
+ public void forceRecognitionEvent(int modelHandle) {
+ mDelegate.forceRecognitionEvent(modelHandle);
+ }
+
+ @Override
+ public int getModelParameter(int modelHandle, int param) {
+ return mDelegate.getModelParameter(modelHandle, param);
+ }
+
+ @Override
+ public void setModelParameter(int modelHandle, int param, int value) {
+ mDelegate.setModelParameter(modelHandle, param, value);
+ }
+
+ @Override
+ public ModelParameterRange queryParameter(int modelHandle, int param) {
+ return mDelegate.queryParameter(modelHandle, param);
+ }
+
+ @Override
+ public String interfaceDescriptor() {
+ return mDelegate.interfaceDescriptor();
+ }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalEnforcer.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalEnforcer.java
new file mode 100644
index 0000000..6870f4f
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalEnforcer.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.soundtrigger_middleware;
+
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Status;
+import android.os.DeadObjectException;
+import android.os.IBinder;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A decorator around a HAL, which adds some checks that the HAL is behaving as expected.
+ * This is not necessarily a strict enforcement for the HAL contract, but a place to add checks for
+ * common HAL malfunctions, to help track them and assist in debugging.
+ *
+ * The class is thread-safe.
+ */
+public class SoundTriggerHalEnforcer implements ISoundTriggerHal {
+ private static final String TAG = "SoundTriggerHalEnforcer";
+
+ /** The state of a model. */
+ private enum ModelState {
+ /** Model is loaded, but inactive. */
+ INACTIVE,
+ /** Model is active. */
+ ACTIVE,
+ /** A request to stop is being made, which may or may not have been processed yet. */
+ PENDING_STOP,
+ }
+
+ private final ISoundTriggerHal mUnderlying;
+ private final Map<Integer, ModelState> mModelStates = new HashMap<>();
+
+ public SoundTriggerHalEnforcer(
+ ISoundTriggerHal underlying) {
+ mUnderlying = underlying;
+ }
+
+ @Override
+ public Properties getProperties() {
+ try {
+ return mUnderlying.getProperties();
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public void registerCallback(GlobalCallback callback) {
+ try {
+ mUnderlying.registerCallback(callback);
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public int loadSoundModel(SoundModel soundModel, ModelCallback callback) {
+ try {
+ synchronized (mModelStates) {
+ int handle = mUnderlying.loadSoundModel(soundModel,
+ new ModelCallbackEnforcer(callback));
+ mModelStates.put(handle, ModelState.INACTIVE);
+ return handle;
+ }
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public int loadPhraseSoundModel(PhraseSoundModel soundModel, ModelCallback callback) {
+ try {
+ synchronized (mModelStates) {
+ int handle = mUnderlying.loadPhraseSoundModel(soundModel,
+ new ModelCallbackEnforcer(callback));
+ mModelStates.put(handle, ModelState.INACTIVE);
+ return handle;
+ }
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public void unloadSoundModel(int modelHandle) {
+ try {
+ // This call into the HAL may block on callback processing, thus must be done outside
+ // of the critical section. After this call returns we are guaranteed to no longer be
+ // getting unload events for that model.
+ mUnderlying.unloadSoundModel(modelHandle);
+ synchronized (mModelStates) {
+ // At this point, the model may have already been removed by a HAL callback, but the
+ // remove() method is a no-op in this case, so thus safe.
+ mModelStates.remove(modelHandle);
+ }
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public void stopRecognition(int modelHandle) {
+ try {
+ // This call into the HAL may block on callback processing, thus must be done outside
+ // of the critical section. After this call returns we are guaranteed to no longer be
+ // getting stop events for that model.
+ synchronized (mModelStates) {
+ mModelStates.replace(modelHandle, ModelState.PENDING_STOP);
+ }
+ mUnderlying.stopRecognition(modelHandle);
+ synchronized (mModelStates) {
+ // At this point, the model might have been preemptively unloaded, but replace()
+ // do nothing when the entry does not exist, so all good.
+ mModelStates.replace(modelHandle, ModelState.INACTIVE);
+ }
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public void startRecognition(int modelHandle, int deviceHandle, int ioHandle,
+ RecognitionConfig config) {
+ try {
+ synchronized (mModelStates) {
+ mUnderlying.startRecognition(modelHandle, deviceHandle, ioHandle, config);
+ mModelStates.replace(modelHandle, ModelState.ACTIVE);
+ }
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public void forceRecognitionEvent(int modelHandle) {
+ try {
+ mUnderlying.forceRecognitionEvent(modelHandle);
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public int getModelParameter(int modelHandle, int param) {
+ try {
+ return mUnderlying.getModelParameter(modelHandle, param);
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public void setModelParameter(int modelHandle, int param, int value) {
+ try {
+ mUnderlying.setModelParameter(modelHandle, param, value);
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public ModelParameterRange queryParameter(int modelHandle, int param) {
+ try {
+ return mUnderlying.queryParameter(modelHandle, param);
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public void linkToDeath(IBinder.DeathRecipient recipient) {
+ mUnderlying.linkToDeath(recipient);
+ }
+
+ @Override
+ public void unlinkToDeath(IBinder.DeathRecipient recipient) {
+ mUnderlying.unlinkToDeath(recipient);
+ }
+
+ @Override
+ public String interfaceDescriptor() {
+ return mUnderlying.interfaceDescriptor();
+ }
+
+ @Override
+ public void flushCallbacks() {
+ mUnderlying.flushCallbacks();
+ }
+
+ private RuntimeException handleException(RuntimeException e) {
+ if (e instanceof RecoverableException) {
+ throw e;
+ }
+ if (e.getCause() instanceof DeadObjectException) {
+ // Server is dead, no need to reboot.
+ Log.e(TAG, "HAL died");
+ throw new RecoverableException(Status.DEAD_OBJECT);
+ }
+ Log.e(TAG, "Exception caught from HAL, rebooting HAL");
+ reboot();
+ throw e;
+ }
+
+ @Override
+ public void reboot() {
+ mUnderlying.reboot();
+ }
+
+ @Override
+ public void detach() {
+ mUnderlying.detach();
+ }
+
+ private class ModelCallbackEnforcer implements ModelCallback {
+ private final ModelCallback mUnderlying;
+
+ private ModelCallbackEnforcer(
+ ModelCallback underlying) {
+ mUnderlying = underlying;
+ }
+
+ @Override
+ public void recognitionCallback(int model, RecognitionEvent event) {
+ int status = event.status;
+
+ synchronized (mModelStates) {
+ ModelState state = mModelStates.get(model);
+ if (state == null || state == ModelState.INACTIVE) {
+ Log.wtfStack(TAG, "Unexpected recognition event for model: " + model);
+ reboot();
+ return;
+ }
+ if (status != RecognitionStatus.FORCED) {
+ mModelStates.replace(model, ModelState.INACTIVE);
+ }
+ }
+ // Always invoke the delegate from outside the critical section.
+ mUnderlying.recognitionCallback(model, event);
+ }
+
+ @Override
+ public void phraseRecognitionCallback(int model, PhraseRecognitionEvent event) {
+ int status = event.common.status;
+ synchronized (mModelStates) {
+ ModelState state = mModelStates.get(model);
+ if (state == null || state == ModelState.INACTIVE) {
+ Log.wtfStack(TAG, "Unexpected recognition event for model: " + model);
+ reboot();
+ return;
+ }
+ if (status != RecognitionStatus.FORCED) {
+ mModelStates.replace(model, ModelState.INACTIVE);
+ }
+ }
+ // Always invoke the delegate from outside the critical section.
+ mUnderlying.phraseRecognitionCallback(model, event);
+ }
+
+ @Override
+ public void modelUnloaded(int modelHandle) {
+ synchronized (mModelStates) {
+ ModelState state = mModelStates.get(modelHandle);
+ if (state == null) {
+ Log.wtfStack(TAG, "Unexpected unload event for model: " + modelHandle);
+ reboot();
+ return;
+ }
+
+ if (state == ModelState.ACTIVE) {
+ Log.wtfStack(TAG, "Trying to unload an active model: " + modelHandle);
+ reboot();
+ return;
+ }
+ mModelStates.remove(modelHandle);
+ }
+ // Always invoke the delegate from outside the critical section.
+ mUnderlying.modelUnloaded(modelHandle);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalMaxModelLimiter.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalMaxModelLimiter.java
new file mode 100644
index 0000000..7dd28e0
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalMaxModelLimiter.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Status;
+import android.os.IBinder;
+
+/**
+ * This is a decorator around ISoundTriggerHal, which implements enforcement of the maximum number
+ * of models supported by the HAL, for HAL implementations older than V2.4 that do not support
+ * rejection of model loading at the HAL layer.
+ * Since preemptive model unloading has been introduced in V2.4, it should never be used in
+ * conjunction with this class, hence we don't bother considering preemtive unloading when counting
+ * the number of currently loaded models.
+ */
+public class SoundTriggerHalMaxModelLimiter implements ISoundTriggerHal {
+ private final @NonNull ISoundTriggerHal mDelegate;
+ private final int mMaxModels;
+
+ // This counter is used to enforce the maximum number of loaded models.
+ private int mNumLoadedModels = 0;
+
+ private GlobalCallback mGlobalCallback;
+
+ public SoundTriggerHalMaxModelLimiter(
+ ISoundTriggerHal delegate, int maxModels) {
+ mDelegate = delegate;
+ this.mMaxModels = maxModels;
+ }
+
+ @Override
+ public void reboot() {
+ mDelegate.reboot();
+ }
+
+ @Override
+ public void detach() {
+ mDelegate.detach();
+ }
+
+ @Override
+ public Properties getProperties() {
+ return mDelegate.getProperties();
+ }
+
+ @Override
+ public void registerCallback(GlobalCallback callback) {
+ mGlobalCallback = callback;
+ mDelegate.registerCallback(mGlobalCallback);
+ }
+
+ @Override
+ public int loadSoundModel(SoundModel soundModel, ModelCallback callback) {
+ synchronized (this) {
+ if (mNumLoadedModels == mMaxModels) {
+ throw new RecoverableException(Status.RESOURCE_CONTENTION);
+ }
+ int result = mDelegate.loadSoundModel(soundModel, callback);
+ ++mNumLoadedModels;
+ return result;
+ }
+ }
+
+ @Override
+ public int loadPhraseSoundModel(PhraseSoundModel soundModel,
+ ModelCallback callback) {
+ synchronized (this) {
+ if (mNumLoadedModels == mMaxModels) {
+ throw new RecoverableException(Status.RESOURCE_CONTENTION);
+ }
+ int result = mDelegate.loadPhraseSoundModel(soundModel, callback);
+ ++mNumLoadedModels;
+ return result;
+ }
+ }
+
+ @Override
+ public void unloadSoundModel(int modelHandle) {
+ boolean wasAtMaxCapacity;
+ synchronized (this) {
+ wasAtMaxCapacity = mNumLoadedModels-- == mMaxModels;
+ }
+ try {
+ mDelegate.unloadSoundModel(modelHandle);
+ } catch (Exception e) {
+ synchronized (this) {
+ ++mNumLoadedModels;
+ }
+ throw e;
+ }
+ if (wasAtMaxCapacity) {
+ // It is legal to invoke callbacks from within unloadSoundModel().
+ // See README.md for details.
+ mGlobalCallback.onResourcesAvailable();
+ }
+ }
+
+ @Override
+ public void stopRecognition(int modelHandle) {
+ mDelegate.stopRecognition(modelHandle);
+ }
+
+ @Override
+ public void startRecognition(int modelHandle, int deviceHandle, int ioHandle,
+ RecognitionConfig config) {
+ mDelegate.startRecognition(modelHandle, deviceHandle, ioHandle, config);
+ }
+
+ @Override
+ public void forceRecognitionEvent(int modelHandle) {
+ mDelegate.forceRecognitionEvent(modelHandle);
+ }
+
+ @Override
+ public int getModelParameter(int modelHandle, int param) {
+ return mDelegate.getModelParameter(modelHandle, param);
+ }
+
+ @Override
+ public void setModelParameter(int modelHandle, int param, int value) {
+ mDelegate.setModelParameter(modelHandle, param, value);
+ }
+
+ @Override
+ public ModelParameterRange queryParameter(int modelHandle, int param) {
+ return mDelegate.queryParameter(modelHandle, param);
+ }
+
+ @Override
+ public void linkToDeath(IBinder.DeathRecipient recipient) {
+ mDelegate.linkToDeath(recipient);
+ }
+
+ @Override
+ public void unlinkToDeath(IBinder.DeathRecipient recipient) {
+ mDelegate.unlinkToDeath(recipient);
+ }
+
+ @Override
+ public String interfaceDescriptor() {
+ return mDelegate.interfaceDescriptor();
+ }
+
+ @Override
+ public void flushCallbacks() {
+ mDelegate.flushCallbacks();
+ }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Watchdog.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalWatchdog.java
similarity index 64%
rename from services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Watchdog.java
rename to services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalWatchdog.java
index 212f81f..5fe06ee 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Watchdog.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalWatchdog.java
@@ -17,13 +17,12 @@
package com.android.server.soundtrigger_middleware;
import android.annotation.NonNull;
-import android.hardware.soundtrigger.V2_1.ISoundTriggerHw;
-import android.hardware.soundtrigger.V2_3.ModelParameterRange;
-import android.hardware.soundtrigger.V2_3.Properties;
-import android.hardware.soundtrigger.V2_3.RecognitionConfig;
-import android.os.IHwBinder;
-import android.os.RemoteException;
-import android.os.SystemProperties;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.SoundModel;
+import android.os.IBinder;
import android.util.Log;
import java.util.Objects;
@@ -31,21 +30,19 @@
import java.util.TimerTask;
/**
- * An {@link ISoundTriggerHw2} decorator that would enforce deadlines on all calls and reboot the
+ * An {@link ISoundTriggerHal} decorator that would enforce deadlines on all calls and reboot the
* HAL whenever they expire.
*/
-public class SoundTriggerHw2Watchdog implements ISoundTriggerHw2 {
+public class SoundTriggerHalWatchdog implements ISoundTriggerHal {
private static final long TIMEOUT_MS = 3000;
- private static final String TAG = "SoundTriggerHw2Watchdog";
+ private static final String TAG = "SoundTriggerHalWatchdog";
- private final @NonNull
- ISoundTriggerHw2 mUnderlying;
- private final @NonNull
- Timer mTimer;
+ private final @NonNull ISoundTriggerHal mUnderlying;
+ private final @NonNull Timer mTimer;
- public SoundTriggerHw2Watchdog(@NonNull ISoundTriggerHw2 underlying) {
+ public SoundTriggerHalWatchdog(@NonNull ISoundTriggerHal underlying) {
mUnderlying = Objects.requireNonNull(underlying);
- mTimer = new Timer("SoundTriggerHw2Watchdog");
+ mTimer = new Timer("SoundTriggerHalWatchdog");
}
@Override
@@ -56,18 +53,24 @@
}
@Override
- public int loadSoundModel(ISoundTriggerHw.SoundModel soundModel, Callback callback,
- int cookie) {
+ public void registerCallback(GlobalCallback callback) {
try (Watchdog ignore = new Watchdog()) {
- return mUnderlying.loadSoundModel(soundModel, callback, cookie);
+ mUnderlying.registerCallback(callback);
}
}
@Override
- public int loadPhraseSoundModel(ISoundTriggerHw.PhraseSoundModel soundModel, Callback callback,
- int cookie) {
+ public int loadSoundModel(SoundModel soundModel, ModelCallback callback) {
try (Watchdog ignore = new Watchdog()) {
- return mUnderlying.loadPhraseSoundModel(soundModel, callback, cookie);
+ return mUnderlying.loadSoundModel(soundModel, callback);
+ }
+ }
+
+ @Override
+ public int loadPhraseSoundModel(PhraseSoundModel soundModel,
+ ModelCallback callback) {
+ try (Watchdog ignore = new Watchdog()) {
+ return mUnderlying.loadPhraseSoundModel(soundModel, callback);
}
}
@@ -86,24 +89,17 @@
}
@Override
- public void stopAllRecognitions() {
+ public void startRecognition(int modelHandle, int deviceHandle, int ioHandle,
+ RecognitionConfig config) {
try (Watchdog ignore = new Watchdog()) {
- mUnderlying.stopAllRecognitions();
+ mUnderlying.startRecognition(modelHandle, deviceHandle, ioHandle, config);
}
}
@Override
- public void startRecognition(int modelHandle, RecognitionConfig config, Callback callback,
- int cookie) {
+ public void forceRecognitionEvent(int modelHandle) {
try (Watchdog ignore = new Watchdog()) {
- mUnderlying.startRecognition(modelHandle, config, callback, cookie);
- }
- }
-
- @Override
- public void getModelState(int modelHandle) {
- try (Watchdog ignore = new Watchdog()) {
- mUnderlying.getModelState(modelHandle);
+ mUnderlying.forceRecognitionEvent(modelHandle);
}
}
@@ -129,23 +125,33 @@
}
@Override
- public boolean linkToDeath(IHwBinder.DeathRecipient recipient, long cookie) {
- return mUnderlying.linkToDeath(recipient, cookie);
+ public void linkToDeath(IBinder.DeathRecipient recipient) {
+ mUnderlying.linkToDeath(recipient);
}
@Override
- public boolean unlinkToDeath(IHwBinder.DeathRecipient recipient) {
- return mUnderlying.unlinkToDeath(recipient);
+ public void unlinkToDeath(IBinder.DeathRecipient recipient) {
+ mUnderlying.unlinkToDeath(recipient);
}
@Override
- public String interfaceDescriptor() throws RemoteException {
+ public String interfaceDescriptor() {
return mUnderlying.interfaceDescriptor();
}
- private static void rebootHal() {
- // This property needs to be defined in an init.rc script and trigger a HAL reboot.
- SystemProperties.set("sys.audio.restart.hal", "1");
+ @Override
+ public void flushCallbacks() {
+ mUnderlying.flushCallbacks();
+ }
+
+ @Override
+ public void reboot() {
+ mUnderlying.reboot();
+ }
+
+ @Override
+ public void detach() {
+ mUnderlying.detach();
}
private class Watchdog implements AutoCloseable {
@@ -160,7 +166,7 @@
@Override
public void run() {
Log.e(TAG, "HAL deadline expired. Rebooting.", mException);
- rebootHal();
+ reboot();
}
};
mTimer.schedule(mTask, TIMEOUT_MS);
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
index 2f087f4..7a1f775 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
@@ -18,60 +18,116 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.media.soundtrigger_middleware.Status;
+import android.hardware.soundtrigger.V2_0.ISoundTriggerHw;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Status;
+import android.os.IBinder;
import android.os.IHwBinder;
import android.os.RemoteException;
+import android.system.OsConstants;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
/**
- * An implementation of {@link ISoundTriggerHw2}, on top of any
+ * An implementation of {@link ISoundTriggerHal}, on top of any
* android.hardware.soundtrigger.V2_x.ISoundTriggerHw implementation. This class hides away some of
* the details involved with retaining backward compatibility and adapts to the more pleasant syntax
- * exposed by {@link ISoundTriggerHw2}, compared to the bare driver interface.
+ * exposed by {@link ISoundTriggerHal}, compared to the bare driver interface.
* <p>
* Exception handling:
* <ul>
* <li>All {@link RemoteException}s get rethrown as {@link RuntimeException}.
* <li>All HAL malfunctions get thrown as {@link HalException}.
* <li>All unsupported operations get thrown as {@link RecoverableException} with a
- * {@link android.media.soundtrigger_middleware.Status#OPERATION_NOT_SUPPORTED}
+ * {@link android.media.soundtrigger.Status#OPERATION_NOT_SUPPORTED}
* code.
* </ul>
*/
-final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
- private final @NonNull
- IHwBinder mBinder;
- private final @NonNull
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw mUnderlying_2_0;
- private final @Nullable
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw mUnderlying_2_1;
- private final @Nullable
- android.hardware.soundtrigger.V2_2.ISoundTriggerHw mUnderlying_2_2;
- private final @Nullable
- android.hardware.soundtrigger.V2_3.ISoundTriggerHw mUnderlying_2_3;
+final class SoundTriggerHw2Compat implements ISoundTriggerHal {
+ private final @NonNull Runnable mRebootRunnable;
+ private final @NonNull IHwBinder mBinder;
+ private @NonNull android.hardware.soundtrigger.V2_0.ISoundTriggerHw mUnderlying_2_0;
+ private @Nullable android.hardware.soundtrigger.V2_1.ISoundTriggerHw mUnderlying_2_1;
+ private @Nullable android.hardware.soundtrigger.V2_2.ISoundTriggerHw mUnderlying_2_2;
+ private @Nullable android.hardware.soundtrigger.V2_3.ISoundTriggerHw mUnderlying_2_3;
+ private @Nullable android.hardware.soundtrigger.V2_4.ISoundTriggerHw mUnderlying_2_4;
- public SoundTriggerHw2Compat(
- @NonNull android.hardware.soundtrigger.V2_0.ISoundTriggerHw underlying) {
- this(underlying.asBinder());
+ // HAL <=2.1 requires us to pass a callback argument to startRecognition. We will store the one
+ // passed on load and then pass it on start. We don't bother storing the callback on newer
+ // versions.
+ private final @NonNull ConcurrentMap<Integer, ModelCallback> mModelCallbacks =
+ new ConcurrentHashMap<>();
+
+ // A map from IBinder.DeathRecipient to IHwBinder.DeathRecipient for doing the mapping upon
+ // unlinking.
+ private final @NonNull Map<IBinder.DeathRecipient, IHwBinder.DeathRecipient>
+ mDeathRecipientMap = new HashMap<>();
+
+ // The properties are read at construction time and cached, since we need to use some of them
+ // to enforce constraints.
+ private final @NonNull Properties mProperties;
+
+ static ISoundTriggerHal create(
+ @NonNull ISoundTriggerHw underlying,
+ @NonNull Runnable rebootRunnable,
+ ICaptureStateNotifier notifier) {
+ return create(underlying.asBinder(), rebootRunnable, notifier);
}
- public SoundTriggerHw2Compat(IHwBinder binder) {
- Objects.requireNonNull(binder);
+ static ISoundTriggerHal create(@NonNull IHwBinder binder,
+ @NonNull Runnable rebootRunnable,
+ ICaptureStateNotifier notifier) {
+ SoundTriggerHw2Compat compat = new SoundTriggerHw2Compat(binder, rebootRunnable);
+ ISoundTriggerHal result = compat;
+ // Add max model limiter for versions <2.4.
+ if (compat.mUnderlying_2_4 == null) {
+ result = new SoundTriggerHalMaxModelLimiter(result,
+ compat.mProperties.maxSoundModels);
+ }
+ // Add concurrent capture handler for versions <2.4 which do not support concurrent capture.
+ if (compat.mUnderlying_2_4 == null && !compat.mProperties.concurrentCapture) {
+ result = new SoundTriggerHalConcurrentCaptureHandler(result, notifier);
+ }
+ return result;
+ }
- mBinder = binder;
+ private SoundTriggerHw2Compat(@NonNull IHwBinder binder, @NonNull Runnable rebootRunnable) {
+ mRebootRunnable = Objects.requireNonNull(rebootRunnable);
+ mBinder = Objects.requireNonNull(binder);
+ initUnderlying(binder);
+ mProperties = Objects.requireNonNull(getPropertiesInternal());
+ }
+ private void initUnderlying(IHwBinder binder) {
// We want to share the proxy instances rather than create a separate proxy for every
// version, so we go down the versions in descending order to find the latest one supported,
// and then simply up-cast it to obtain all the versions that are earlier.
+ // Attempt 2.4
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHw as2_4 =
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHw.asInterface(binder);
+ if (as2_4 != null) {
+ mUnderlying_2_0 =
+ mUnderlying_2_1 = mUnderlying_2_2 = mUnderlying_2_3 = mUnderlying_2_4 = as2_4;
+ return;
+ }
+
// Attempt 2.3
android.hardware.soundtrigger.V2_3.ISoundTriggerHw as2_3 =
android.hardware.soundtrigger.V2_3.ISoundTriggerHw.asInterface(binder);
if (as2_3 != null) {
mUnderlying_2_0 = mUnderlying_2_1 = mUnderlying_2_2 = mUnderlying_2_3 = as2_3;
+ mUnderlying_2_4 = null;
return;
}
@@ -80,7 +136,7 @@
android.hardware.soundtrigger.V2_2.ISoundTriggerHw.asInterface(binder);
if (as2_2 != null) {
mUnderlying_2_0 = mUnderlying_2_1 = mUnderlying_2_2 = as2_2;
- mUnderlying_2_3 = null;
+ mUnderlying_2_3 = mUnderlying_2_4 = null;
return;
}
@@ -89,7 +145,7 @@
android.hardware.soundtrigger.V2_1.ISoundTriggerHw.asInterface(binder);
if (as2_1 != null) {
mUnderlying_2_0 = mUnderlying_2_1 = as2_1;
- mUnderlying_2_2 = mUnderlying_2_3 = null;
+ mUnderlying_2_2 = mUnderlying_2_3 = mUnderlying_2_4 = null;
return;
}
@@ -98,7 +154,7 @@
android.hardware.soundtrigger.V2_0.ISoundTriggerHw.asInterface(binder);
if (as2_0 != null) {
mUnderlying_2_0 = as2_0;
- mUnderlying_2_1 = mUnderlying_2_2 = mUnderlying_2_3 = null;
+ mUnderlying_2_1 = mUnderlying_2_2 = mUnderlying_2_3 = mUnderlying_2_4 = null;
return;
}
@@ -111,12 +167,32 @@
}
}
+ private static void handleHalStatusAllowBusy(int status, String methodName) {
+ if (status == -OsConstants.EBUSY) {
+ throw new RecoverableException(Status.RESOURCE_CONTENTION);
+ }
+ handleHalStatus(status, methodName);
+ }
+
@Override
- public android.hardware.soundtrigger.V2_3.Properties getProperties() {
+ public void reboot() {
+ mRebootRunnable.run();
+ }
+
+ @Override
+ public void detach() {
+ // No-op.
+ }
+
+ @Override
+ public Properties getProperties() {
+ return mProperties;
+ }
+
+ private Properties getPropertiesInternal() {
try {
AtomicInteger retval = new AtomicInteger(-1);
- AtomicReference<android.hardware.soundtrigger.V2_3.Properties>
- properties =
+ AtomicReference<android.hardware.soundtrigger.V2_3.Properties> properties =
new AtomicReference<>();
try {
as2_3().getProperties_2_3(
@@ -129,30 +205,57 @@
return getProperties_2_0();
}
handleHalStatus(retval.get(), "getProperties_2_3");
- return properties.get();
+ return ConversionUtil.hidl2aidlProperties(properties.get());
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
@Override
- public int loadSoundModel(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel soundModel,
- Callback callback, int cookie) {
+ public void registerCallback(GlobalCallback callback) {
+ try {
+ try {
+ as2_4().registerGlobalCallback(new GlobalCallbackWrapper(callback));
+ } catch (NotSupported e) {
+ // In versions < 2.4 the events represented by this callback don't exist, we can
+ // safely ignore this.
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public int loadSoundModel(SoundModel soundModel, ModelCallback callback) {
+ android.hardware.soundtrigger.V2_3.ISoundTriggerHw.SoundModel hidlModel =
+ ConversionUtil.aidl2hidlSoundModel(soundModel);
try {
AtomicInteger retval = new AtomicInteger(-1);
AtomicInteger handle = new AtomicInteger(0);
+
try {
- as2_1().loadSoundModel_2_1(soundModel, new SoundTriggerCallback(callback), cookie,
+ as2_4().loadSoundModel_2_4(hidlModel, new ModelCallbackWrapper(callback),
(r, h) -> {
retval.set(r);
handle.set(h);
});
+ handleHalStatusAllowBusy(retval.get(), "loadSoundModel_2_4");
} catch (NotSupported e) {
- // Fall-back to the 2.0 version:
- return loadSoundModel_2_0(soundModel, callback, cookie);
+ // Fall-back to the 2.1 version:
+ try {
+ as2_1().loadSoundModel_2_1(hidlModel, new ModelCallbackWrapper(callback),
+ 0,
+ (r, h) -> {
+ retval.set(r);
+ handle.set(h);
+ });
+ handleHalStatus(retval.get(), "loadSoundModel_2_1");
+ mModelCallbacks.put(handle.get(), callback);
+ } catch (NotSupported ee) {
+ // Fall-back to the 2.0 version:
+ return loadSoundModel_2_0(hidlModel, callback);
+ }
}
- handleHalStatus(retval.get(), "loadSoundModel_2_1");
return handle.get();
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
@@ -160,24 +263,35 @@
}
@Override
- public int loadPhraseSoundModel(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel soundModel,
- Callback callback, int cookie) {
+ public int loadPhraseSoundModel(PhraseSoundModel soundModel, ModelCallback callback) {
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel hidlModel =
+ ConversionUtil.aidl2hidlPhraseSoundModel(soundModel);
try {
AtomicInteger retval = new AtomicInteger(-1);
AtomicInteger handle = new AtomicInteger(0);
try {
- as2_1().loadPhraseSoundModel_2_1(soundModel, new SoundTriggerCallback(callback),
- cookie,
+ as2_4().loadPhraseSoundModel_2_4(hidlModel, new ModelCallbackWrapper(callback),
(r, h) -> {
retval.set(r);
handle.set(h);
});
+ handleHalStatusAllowBusy(retval.get(), "loadPhraseSoundModel_2_4");
} catch (NotSupported e) {
- // Fall-back to the 2.0 version:
- return loadPhraseSoundModel_2_0(soundModel, callback, cookie);
+ // Fall-back to the 2.1 version:
+ try {
+ as2_1().loadPhraseSoundModel_2_1(hidlModel, new ModelCallbackWrapper(callback),
+ 0,
+ (r, h) -> {
+ retval.set(r);
+ handle.set(h);
+ });
+ handleHalStatus(retval.get(), "loadPhraseSoundModel_2_1");
+ mModelCallbacks.put(handle.get(), callback);
+ } catch (NotSupported ee) {
+ // Fall-back to the 2.0 version:
+ return loadPhraseSoundModel_2_0(hidlModel, callback);
+ }
}
- handleHalStatus(retval.get(), "loadSoundModel_2_1");
return handle.get();
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
@@ -187,6 +301,8 @@
@Override
public void unloadSoundModel(int modelHandle) {
try {
+ // Safe if key doesn't exist.
+ mModelCallbacks.remove(modelHandle);
int retval = as2_0().unloadSoundModel(modelHandle);
handleHalStatus(retval, "unloadSoundModel");
} catch (RemoteException e) {
@@ -206,26 +322,23 @@
}
@Override
- public void stopAllRecognitions() {
- try {
- int retval = as2_0().stopAllRecognitions();
- handleHalStatus(retval, "stopAllRecognitions");
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
- }
-
- @Override
- public void startRecognition(int modelHandle,
- android.hardware.soundtrigger.V2_3.RecognitionConfig config,
- Callback callback, int cookie) {
+ public void startRecognition(int modelHandle, int deviceHandle, int ioHandle,
+ RecognitionConfig config) {
+ android.hardware.soundtrigger.V2_3.RecognitionConfig hidlConfig =
+ ConversionUtil.aidl2hidlRecognitionConfig(config, deviceHandle, ioHandle);
try {
try {
- int retval = as2_3().startRecognition_2_3(modelHandle, config);
- handleHalStatus(retval, "startRecognition_2_3");
+ int retval = as2_4().startRecognition_2_4(modelHandle, hidlConfig);
+ handleHalStatusAllowBusy(retval, "startRecognition_2_4");
} catch (NotSupported e) {
- // Fall-back to the 2.0 version:
- startRecognition_2_1(modelHandle, config, callback, cookie);
+ // Fall-back to the 2.3 version:
+ try {
+ int retval = as2_3().startRecognition_2_3(modelHandle, hidlConfig);
+ handleHalStatus(retval, "startRecognition_2_3");
+ } catch (NotSupported ee) {
+ // Fall-back to the 2.0 version:
+ startRecognition_2_1(modelHandle, hidlConfig);
+ }
}
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
@@ -233,7 +346,7 @@
}
@Override
- public void getModelState(int modelHandle) {
+ public void forceRecognitionEvent(int modelHandle) {
try {
int retval = as2_2().getModelState(modelHandle);
handleHalStatus(retval, "getModelState");
@@ -276,8 +389,7 @@
}
@Override
- public android.hardware.soundtrigger.V2_3.ModelParameterRange queryParameter(int modelHandle,
- int param) {
+ public ModelParameterRange queryParameter(int modelHandle, int param) {
AtomicInteger status = new AtomicInteger(-1);
AtomicReference<android.hardware.soundtrigger.V2_3.OptionalModelParameterRange>
optionalRange =
@@ -298,25 +410,36 @@
return (optionalRange.get().getDiscriminator()
== android.hardware.soundtrigger.V2_3.OptionalModelParameterRange.hidl_discriminator.range)
?
- optionalRange.get().range() : null;
+ ConversionUtil.hidl2aidlModelParameterRange(optionalRange.get().range()) : null;
}
@Override
- public boolean linkToDeath(IHwBinder.DeathRecipient recipient, long cookie) {
- return mBinder.linkToDeath(recipient, cookie);
+ public void linkToDeath(IBinder.DeathRecipient recipient) {
+ IHwBinder.DeathRecipient wrapper = cookie -> recipient.binderDied();
+ mDeathRecipientMap.put(recipient, wrapper);
+ mBinder.linkToDeath(wrapper, 0);
}
@Override
- public boolean unlinkToDeath(IHwBinder.DeathRecipient recipient) {
- return mBinder.unlinkToDeath(recipient);
+ public void unlinkToDeath(IBinder.DeathRecipient recipient) {
+ mBinder.unlinkToDeath(mDeathRecipientMap.remove(recipient));
}
@Override
- public String interfaceDescriptor() throws RemoteException {
- return as2_0().interfaceDescriptor();
+ public String interfaceDescriptor() {
+ try {
+ return as2_0().interfaceDescriptor();
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
}
- private android.hardware.soundtrigger.V2_3.Properties getProperties_2_0()
+ @Override
+ public void flushCallbacks() {
+ // This is a no-op. Only implemented for decorators.
+ }
+
+ private Properties getProperties_2_0()
throws RemoteException {
AtomicInteger retval = new AtomicInteger(-1);
AtomicReference<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties>
@@ -328,12 +451,13 @@
properties.set(p);
});
handleHalStatus(retval.get(), "getProperties");
- return Hw2CompatUtil.convertProperties_2_0_to_2_3(properties.get());
+ return ConversionUtil.hidl2aidlProperties(
+ Hw2CompatUtil.convertProperties_2_0_to_2_3(properties.get()));
}
private int loadSoundModel_2_0(
android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel soundModel,
- Callback callback, int cookie)
+ ModelCallback callback)
throws RemoteException {
// Convert the soundModel to V2.0.
android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel model_2_0 =
@@ -341,17 +465,18 @@
AtomicInteger retval = new AtomicInteger(-1);
AtomicInteger handle = new AtomicInteger(0);
- as2_0().loadSoundModel(model_2_0, new SoundTriggerCallback(callback), cookie, (r, h) -> {
+ as2_0().loadSoundModel(model_2_0, new ModelCallbackWrapper(callback), 0, (r, h) -> {
retval.set(r);
handle.set(h);
});
handleHalStatus(retval.get(), "loadSoundModel");
+ mModelCallbacks.put(handle.get(), callback);
return handle.get();
}
private int loadPhraseSoundModel_2_0(
android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel soundModel,
- Callback callback, int cookie)
+ ModelCallback callback)
throws RemoteException {
// Convert the soundModel to V2.0.
android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel model_2_0 =
@@ -359,28 +484,28 @@
AtomicInteger retval = new AtomicInteger(-1);
AtomicInteger handle = new AtomicInteger(0);
- as2_0().loadPhraseSoundModel(model_2_0, new SoundTriggerCallback(callback), cookie,
+ as2_0().loadPhraseSoundModel(model_2_0, new ModelCallbackWrapper(callback), 0,
(r, h) -> {
retval.set(r);
handle.set(h);
});
handleHalStatus(retval.get(), "loadSoundModel");
+ mModelCallbacks.put(handle.get(), callback);
return handle.get();
}
private void startRecognition_2_1(int modelHandle,
- android.hardware.soundtrigger.V2_3.RecognitionConfig config,
- Callback callback, int cookie) {
+ android.hardware.soundtrigger.V2_3.RecognitionConfig config) {
try {
try {
android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig config_2_1 =
Hw2CompatUtil.convertRecognitionConfig_2_3_to_2_1(config);
int retval = as2_1().startRecognition_2_1(modelHandle, config_2_1,
- new SoundTriggerCallback(callback), cookie);
+ new ModelCallbackWrapper(mModelCallbacks.get(modelHandle)), 0);
handleHalStatus(retval, "startRecognition_2_1");
} catch (NotSupported e) {
// Fall-back to the 2.0 version:
- startRecognition_2_0(modelHandle, config, callback, cookie);
+ startRecognition_2_0(modelHandle, config);
}
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
@@ -388,13 +513,12 @@
}
private void startRecognition_2_0(int modelHandle,
- android.hardware.soundtrigger.V2_3.RecognitionConfig config,
- Callback callback, int cookie)
+ android.hardware.soundtrigger.V2_3.RecognitionConfig config)
throws RemoteException {
android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig config_2_0 =
Hw2CompatUtil.convertRecognitionConfig_2_3_to_2_0(config);
int retval = as2_0().startRecognition(modelHandle, config_2_0,
- new SoundTriggerCallback(callback), cookie);
+ new ModelCallbackWrapper(mModelCallbacks.get(modelHandle)), 0);
handleHalStatus(retval, "startRecognition");
}
@@ -427,6 +551,14 @@
return mUnderlying_2_3;
}
+ private @NonNull
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHw as2_4() throws NotSupported {
+ if (mUnderlying_2_4 == null) {
+ throw new NotSupported("Underlying driver version < 2.4");
+ }
+ return mUnderlying_2_4;
+ }
+
/**
* A checked exception representing the requested interface version not being supported.
* At the public interface layer, use {@link #throwAsRecoverableException()} to propagate it to
@@ -448,28 +580,48 @@
}
}
- private static class SoundTriggerCallback extends
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.Stub {
- private final @NonNull
- Callback mDelegate;
+ private static class GlobalCallbackWrapper extends
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHwGlobalCallback.Stub {
+ private final @NonNull GlobalCallback mDelegate;
- private SoundTriggerCallback(
- @NonNull Callback delegate) {
+ private GlobalCallbackWrapper(@NonNull GlobalCallback delegate) {
+ mDelegate = delegate;
+ }
+
+ @Override
+ public void onResourcesAvailable() {
+ mDelegate.onResourcesAvailable();
+ }
+ }
+
+ private static class ModelCallbackWrapper extends
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHwCallback.Stub {
+ private final @NonNull ModelCallback mDelegate;
+
+ private ModelCallbackWrapper(
+ @NonNull ModelCallback delegate) {
mDelegate = Objects.requireNonNull(delegate);
}
@Override
+ public void modelUnloaded(int modelHandle) {
+ mDelegate.modelUnloaded(modelHandle);
+ }
+
+ @Override
public void recognitionCallback_2_1(
android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent event,
int cookie) {
- mDelegate.recognitionCallback(event, cookie);
+ mDelegate.recognitionCallback(event.header.model,
+ ConversionUtil.hidl2aidlRecognitionEvent(event));
}
@Override
public void phraseRecognitionCallback_2_1(
android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent event,
int cookie) {
- mDelegate.phraseRecognitionCallback(event, cookie);
+ mDelegate.phraseRecognitionCallback(event.common.header.model,
+ ConversionUtil.hidl2aidlPhraseRecognitionEvent(event));
}
@Override
@@ -485,7 +637,7 @@
int cookie) {
android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent event_2_1 =
Hw2CompatUtil.convertRecognitionEvent_2_0_to_2_1(event);
- mDelegate.recognitionCallback(event_2_1, cookie);
+ recognitionCallback_2_1(event_2_1, cookie);
}
@Override
@@ -494,7 +646,7 @@
int cookie) {
android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent
event_2_1 = Hw2CompatUtil.convertPhraseRecognitionEvent_2_0_to_2_1(event);
- mDelegate.phraseRecognitionCallback(event_2_1, cookie);
+ phraseRecognitionCallback_2_1(event_2_1, cookie);
}
@Override
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java
deleted file mode 100644
index cf7460b..0000000
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.soundtrigger_middleware;
-
-import android.hardware.soundtrigger.V2_1.ISoundTriggerHw;
-import android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback;
-import android.hardware.soundtrigger.V2_3.ModelParameterRange;
-import android.hardware.soundtrigger.V2_3.Properties;
-import android.hardware.soundtrigger.V2_3.RecognitionConfig;
-import android.media.soundtrigger_middleware.Status;
-import android.os.DeadObjectException;
-import android.os.IHwBinder;
-import android.os.RemoteException;
-import android.os.SystemProperties;
-import android.util.Log;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * A decorator around a HAL, which adds some checks that the HAL is behaving as expected.
- * This is not necessarily a strict enforcement for the HAL contract, but a place to add checks for
- * common HAL malfunctions, to help track them and assist in debugging.
- *
- * The class is thread-safe.
- */
-public class SoundTriggerHw2Enforcer implements ISoundTriggerHw2 {
- static final String TAG = "SoundTriggerHw2Enforcer";
-
- final ISoundTriggerHw2 mUnderlying;
- Map<Integer, Boolean> mModelStates = new HashMap<>();
-
- public SoundTriggerHw2Enforcer(
- ISoundTriggerHw2 underlying) {
- mUnderlying = underlying;
- }
-
- @Override
- public Properties getProperties() {
- try {
- return mUnderlying.getProperties();
- } catch (RuntimeException e) {
- throw handleException(e);
- }
- }
-
- @Override
- public int loadSoundModel(ISoundTriggerHw.SoundModel soundModel, Callback callback,
- int cookie) {
- try {
- int handle = mUnderlying.loadSoundModel(soundModel, new CallbackEnforcer(callback),
- cookie);
- synchronized (mModelStates) {
- mModelStates.put(handle, false);
- }
- return handle;
- } catch (RuntimeException e) {
- throw handleException(e);
- }
- }
-
- @Override
- public int loadPhraseSoundModel(ISoundTriggerHw.PhraseSoundModel soundModel, Callback callback,
- int cookie) {
- try {
- int handle = mUnderlying.loadPhraseSoundModel(soundModel,
- new CallbackEnforcer(callback),
- cookie);
- synchronized (mModelStates) {
- mModelStates.put(handle, false);
- }
- return handle;
- } catch (RuntimeException e) {
- throw handleException(e);
- }
- }
-
- @Override
- public void unloadSoundModel(int modelHandle) {
- try {
- mUnderlying.unloadSoundModel(modelHandle);
- synchronized (mModelStates) {
- mModelStates.remove(modelHandle);
- }
- } catch (RuntimeException e) {
- throw handleException(e);
- }
- }
-
- @Override
- public void stopRecognition(int modelHandle) {
- try {
- mUnderlying.stopRecognition(modelHandle);
- synchronized (mModelStates) {
- mModelStates.replace(modelHandle, false);
- }
- } catch (RuntimeException e) {
- throw handleException(e);
- }
- }
-
- @Override
- public void stopAllRecognitions() {
- try {
- mUnderlying.stopAllRecognitions();
- synchronized (mModelStates) {
- for (Map.Entry<Integer, Boolean> entry : mModelStates.entrySet()) {
- entry.setValue(false);
- }
- }
- } catch (RuntimeException e) {
- throw handleException(e);
- }
- }
-
- @Override
- public void startRecognition(int modelHandle, RecognitionConfig config, Callback callback,
- int cookie) {
- // It is possible that an event will be sent before the HAL returns from the
- // startRecognition call, thus it is important to set the state to active before the call.
- synchronized (mModelStates) {
- mModelStates.replace(modelHandle, true);
- }
- try {
- mUnderlying.startRecognition(modelHandle, config, new CallbackEnforcer(callback),
- cookie);
- } catch (RuntimeException e) {
- throw handleException(e);
- }
- }
-
- @Override
- public void getModelState(int modelHandle) {
- try {
- mUnderlying.getModelState(modelHandle);
- } catch (RuntimeException e) {
- throw handleException(e);
- }
- }
-
- @Override
- public int getModelParameter(int modelHandle, int param) {
- try {
- return mUnderlying.getModelParameter(modelHandle, param);
- } catch (RuntimeException e) {
- throw handleException(e);
- }
- }
-
- @Override
- public void setModelParameter(int modelHandle, int param, int value) {
- try {
- mUnderlying.setModelParameter(modelHandle, param, value);
- } catch (RuntimeException e) {
- throw handleException(e);
- }
- }
-
- @Override
- public ModelParameterRange queryParameter(int modelHandle, int param) {
- try {
- return mUnderlying.queryParameter(modelHandle, param);
- } catch (RuntimeException e) {
- throw handleException(e);
- }
- }
-
- @Override
- public boolean linkToDeath(IHwBinder.DeathRecipient recipient, long cookie) {
- return mUnderlying.linkToDeath(recipient, cookie);
- }
-
- @Override
- public boolean unlinkToDeath(IHwBinder.DeathRecipient recipient) {
- return mUnderlying.unlinkToDeath(recipient);
- }
-
- @Override
- public String interfaceDescriptor() throws RemoteException {
- return mUnderlying.interfaceDescriptor();
- }
-
- private static RuntimeException handleException(RuntimeException e) {
- if (e.getCause() instanceof DeadObjectException) {
- // Server is dead, no need to reboot.
- Log.e(TAG, "HAL died");
- throw new RecoverableException(Status.DEAD_OBJECT);
- }
- Log.e(TAG, "Exception caught from HAL, rebooting HAL");
- rebootHal();
- throw e;
- }
-
- private static void rebootHal() {
- // This property needs to be defined in an init.rc script and trigger a HAL reboot.
- SystemProperties.set("sys.audio.restart.hal", "1");
- }
-
- private class CallbackEnforcer implements Callback {
- private final Callback mUnderlying;
-
- private CallbackEnforcer(
- Callback underlying) {
- mUnderlying = underlying;
- }
-
- @Override
- public void recognitionCallback(ISoundTriggerHwCallback.RecognitionEvent event,
- int cookie) {
- int model = event.header.model;
- synchronized (mModelStates) {
- if (!mModelStates.getOrDefault(model, false)) {
- Log.wtfStack(TAG, "Unexpected recognition event for model: " + model);
- rebootHal();
- return;
- }
- if (event.header.status
- != android.media.soundtrigger_middleware.RecognitionStatus.FORCED) {
- mModelStates.replace(model, false);
- }
- }
- mUnderlying.recognitionCallback(event, cookie);
- }
-
- @Override
- public void phraseRecognitionCallback(ISoundTriggerHwCallback.PhraseRecognitionEvent event,
- int cookie) {
- int model = event.common.header.model;
- synchronized (mModelStates) {
- if (!mModelStates.getOrDefault(model, false)) {
- Log.wtfStack(TAG, "Unexpected recognition event for model: " + model);
- rebootHal();
- return;
- }
- if (event.common.header.status
- != android.media.soundtrigger_middleware.RecognitionStatus.FORCED) {
- mModelStates.replace(model, false);
- }
- }
- mUnderlying.phraseRecognitionCallback(event, cookie);
- }
- }
-}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw3Compat.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw3Compat.java
new file mode 100644
index 0000000..f564756
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw3Compat.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+import android.hardware.soundtrigger3.ISoundTriggerHw;
+import android.hardware.soundtrigger3.ISoundTriggerHwCallback;
+import android.hardware.soundtrigger3.ISoundTriggerHwGlobalCallback;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Status;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+
+public class SoundTriggerHw3Compat implements ISoundTriggerHal {
+ private final @NonNull ISoundTriggerHw mDriver;
+ private final @NonNull Runnable mRebootRunnable;
+
+ public SoundTriggerHw3Compat(@NonNull IBinder binder, @NonNull Runnable rebootRunnable) {
+ mDriver = android.hardware.soundtrigger3.ISoundTriggerHw.Stub.asInterface(binder);
+ mRebootRunnable = rebootRunnable;
+ }
+
+ @Override
+ public Properties getProperties() {
+ try {
+ return mDriver.getProperties();
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void registerCallback(GlobalCallback callback) {
+ try {
+ mDriver.registerGlobalCallback(new GlobalCallbackAdaper(callback));
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public int loadSoundModel(SoundModel soundModel, ModelCallback callback) {
+ try {
+ return mDriver.loadSoundModel(soundModel, new ModelCallbackAdaper(callback));
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == Status.RESOURCE_CONTENTION) {
+ throw new RecoverableException(Status.RESOURCE_CONTENTION);
+ }
+ throw e;
+ }
+ }
+
+ @Override
+ public int loadPhraseSoundModel(PhraseSoundModel soundModel, ModelCallback callback) {
+ try {
+ return mDriver.loadPhraseSoundModel(soundModel, new ModelCallbackAdaper(callback));
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == Status.RESOURCE_CONTENTION) {
+ throw new RecoverableException(Status.RESOURCE_CONTENTION);
+ }
+ throw e;
+ }
+ }
+
+ @Override
+ public void unloadSoundModel(int modelHandle) {
+ try {
+ mDriver.unloadSoundModel(modelHandle);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void startRecognition(int modelHandle, int deviceHandle, int ioHandle,
+ RecognitionConfig config) {
+ try {
+ mDriver.startRecognition(modelHandle, deviceHandle, ioHandle, config);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == Status.RESOURCE_CONTENTION) {
+ throw new RecoverableException(Status.RESOURCE_CONTENTION);
+ }
+ throw e;
+ }
+ }
+
+ @Override
+ public void stopRecognition(int modelHandle) {
+ try {
+ mDriver.stopRecognition(modelHandle);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void forceRecognitionEvent(int modelHandle) {
+ try {
+ mDriver.forceRecognitionEvent(modelHandle);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public ModelParameterRange queryParameter(int modelHandle, int param) {
+ try {
+ return mDriver.queryParameter(modelHandle, param);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public int getModelParameter(int modelHandle, int param) {
+ try {
+ return mDriver.getParameter(modelHandle, param);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void setModelParameter(int modelHandle, int param, int value) {
+ try {
+ mDriver.setParameter(modelHandle, param, value);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public String interfaceDescriptor() {
+ try {
+ return mDriver.asBinder().getInterfaceDescriptor();
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void linkToDeath(IBinder.DeathRecipient recipient) {
+ try {
+ mDriver.asBinder().linkToDeath(recipient, 0);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void unlinkToDeath(IBinder.DeathRecipient recipient) {
+ mDriver.asBinder().unlinkToDeath(recipient, 0);
+ }
+
+ @Override
+ public void flushCallbacks() {
+ // No-op.
+ }
+
+ @Override
+ public void reboot() {
+ mRebootRunnable.run();
+ }
+
+ @Override
+ public void detach() {
+ // No-op.
+ }
+
+ private static class GlobalCallbackAdaper extends ISoundTriggerHwGlobalCallback.Stub {
+ private final @NonNull GlobalCallback mDelegate;
+
+ public GlobalCallbackAdaper(@NonNull GlobalCallback callback) {
+ mDelegate = callback;
+ }
+
+ @Override
+ public void onResourcesAvailable() {
+ mDelegate.onResourcesAvailable();
+ }
+ }
+
+ private static class ModelCallbackAdaper extends ISoundTriggerHwCallback.Stub {
+ private final @NonNull ModelCallback mDelegate;
+
+ public ModelCallbackAdaper(ModelCallback callback) {
+ mDelegate = callback;
+ }
+
+ @Override
+ public void modelUnloaded(int model) {
+ mDelegate.modelUnloaded(model);
+ }
+
+ @Override
+ public void phraseRecognitionCallback(int model, PhraseRecognitionEvent event) {
+ mDelegate.phraseRecognitionCallback(model, event);
+ }
+
+ @Override
+ public void recognitionCallback(int model, RecognitionEvent event) {
+ mDelegate.recognitionCallback(model, event);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java
index d76b1bf..c8c0f3d 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java
@@ -17,12 +17,9 @@
package com.android.server.soundtrigger_middleware;
import android.annotation.NonNull;
-import android.hardware.soundtrigger.V2_0.ISoundTriggerHw;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
-import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
-import android.os.IBinder;
import android.util.Log;
import java.util.ArrayList;
@@ -39,7 +36,7 @@
* <li>There is no binder instance associated with this implementation. Do not call asBinder().
* <li>The implementation may throw a {@link RecoverableException} to indicate non-fatal,
* recoverable faults. The error code would one of the
- * {@link android.media.soundtrigger_middleware.Status}
+ * {@link android.media.soundtrigger.Status}
* constants. Any other exception thrown should be regarded as a bug in the implementation or one
* of its dependencies (assuming correct usage).
* <li>The implementation is designed for testibility by featuring dependency injection (the
@@ -84,15 +81,15 @@
@NonNull AudioSessionProvider audioSessionProvider) {
List<SoundTriggerModule> modules = new ArrayList<>(halFactories.length);
- for (int i = 0; i < halFactories.length; ++i) {
+ for (HalFactory halFactory : halFactories) {
try {
- modules.add(new SoundTriggerModule(halFactories[i], audioSessionProvider));
+ modules.add(new SoundTriggerModule(halFactory, audioSessionProvider));
} catch (Exception e) {
Log.e(TAG, "Failed to add a SoundTriggerModule instance", e);
}
}
- mModules = modules.toArray(new SoundTriggerModule[modules.size()]);
+ mModules = modules.toArray(new SoundTriggerModule[0]);
}
/**
@@ -122,18 +119,4 @@
ISoundTriggerModule attach(int handle, @NonNull ISoundTriggerCallback callback) {
return mModules[handle].attach(callback);
}
-
- @Override
- public void setCaptureState(boolean active) {
- for (SoundTriggerModule module : mModules) {
- module.setExternalCaptureState(active);
- }
- }
-
- @Override
- public @NonNull
- IBinder asBinder() {
- throw new UnsupportedOperationException(
- "This implementation is not inteded to be used directly with Binder.");
- }
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
index 2ef0759..559e777 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
@@ -18,16 +18,16 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.SoundModel;
import android.media.permission.Identity;
import android.media.permission.IdentityContext;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.SoundModel;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
import android.os.IBinder;
import android.os.RemoteException;
@@ -57,9 +57,10 @@
* }
* }
* </code></pre>
- * The actual handling of these events is then done inside of {@link #logReturnWithObject(Object,
- * String, Object, Object[])}, {@link #logVoidReturnWithObject(Object, String, Object[])} and {@link
- * #logExceptionWithObject(Object, String, Exception, Object[])}.
+ * The actual handling of these events is then done inside of
+ * {@link #logReturnWithObject(Object, Identity, String, Object, Object[])},
+ * {@link #logVoidReturnWithObject(Object, Identity, String, Object[])} and {@link
+ * #logExceptionWithObject(Object, Identity, String, Exception, Object[])}.
*/
public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInternal, Dumpable {
private static final String TAG = "SoundTriggerMiddlewareLogging";
@@ -96,23 +97,6 @@
}
}
- @Override
- public void setCaptureState(boolean active) throws RemoteException {
- try {
- mDelegate.setCaptureState(active);
- logVoidReturn("setCaptureState", active);
- } catch (Exception e) {
- logException("setCaptureState", e, active);
- throw e;
- }
- }
-
- @Override
- public IBinder asBinder() {
- throw new UnsupportedOperationException(
- "This implementation is not inteded to be used directly with Binder.");
- }
-
// Override toString() in order to have the delegate's ID in it.
@Override
public String toString() {
@@ -298,10 +282,10 @@
}
@Override
- public void onRecognition(int modelHandle, RecognitionEvent event)
+ public void onRecognition(int modelHandle, RecognitionEvent event, int captureSession)
throws RemoteException {
try {
- mCallbackDelegate.onRecognition(modelHandle, event);
+ mCallbackDelegate.onRecognition(modelHandle, event, captureSession);
logVoidReturn("onRecognition", modelHandle, event);
} catch (Exception e) {
logException("onRecognition", e, modelHandle, event);
@@ -310,10 +294,11 @@
}
@Override
- public void onPhraseRecognition(int modelHandle, PhraseRecognitionEvent event)
+ public void onPhraseRecognition(int modelHandle, PhraseRecognitionEvent event,
+ int captureSession)
throws RemoteException {
try {
- mCallbackDelegate.onPhraseRecognition(modelHandle, event);
+ mCallbackDelegate.onPhraseRecognition(modelHandle, event, captureSession);
logVoidReturn("onPhraseRecognition", modelHandle, event);
} catch (Exception e) {
logException("onPhraseRecognition", e, modelHandle, event);
@@ -322,12 +307,23 @@
}
@Override
- public void onRecognitionAvailabilityChange(boolean available) throws RemoteException {
+ public void onModelUnloaded(int modelHandle) throws RemoteException {
try {
- mCallbackDelegate.onRecognitionAvailabilityChange(available);
- logVoidReturn("onRecognitionAvailabilityChange", available);
+ mCallbackDelegate.onModelUnloaded(modelHandle);
+ logVoidReturn("onModelUnloaded", modelHandle);
} catch (Exception e) {
- logException("onRecognitionAvailabilityChange", e, available);
+ logException("onModelUnloaded", e, modelHandle);
+ throw e;
+ }
+ }
+
+ @Override
+ public void onResourcesAvailable() throws RemoteException {
+ try {
+ mCallbackDelegate.onResourcesAvailable();
+ logVoidReturn("onResourcesAvailable");
+ } catch (Exception e) {
+ logException("onResourcesAvailable", e);
throw e;
}
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
index c1f8240..32ba852 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
@@ -24,20 +24,20 @@
import android.app.AppOpsManager;
import android.content.Context;
import android.content.PermissionChecker;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Status;
import android.media.permission.Identity;
import android.media.permission.IdentityContext;
import android.media.permission.PermissionUtil;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.SoundModel;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
-import android.media.soundtrigger_middleware.Status;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
@@ -90,24 +90,12 @@
return wrapper.attach(mDelegate.attach(handle, wrapper.getCallbackWrapper()));
}
- @Override
- public void setCaptureState(boolean active) throws RemoteException {
- // This is an internal call. No permissions needed.
- mDelegate.setCaptureState(active);
- }
-
// Override toString() in order to have the delegate's ID in it.
@Override
public String toString() {
return Objects.toString(mDelegate);
}
- @Override
- public IBinder asBinder() {
- throw new UnsupportedOperationException(
- "This implementation is not inteded to be used directly with Binder.");
- }
-
/**
* Get the identity context, or throws an InternalServerError if it has not been established.
*
@@ -313,22 +301,28 @@
}
@Override
- public void onRecognition(int modelHandle, RecognitionEvent event)
+ public void onRecognition(int modelHandle, RecognitionEvent event, int captureSession)
throws RemoteException {
enforcePermissions("Sound trigger recognition.");
- mDelegate.onRecognition(modelHandle, event);
+ mDelegate.onRecognition(modelHandle, event, captureSession);
}
@Override
- public void onPhraseRecognition(int modelHandle, PhraseRecognitionEvent event)
+ public void onPhraseRecognition(int modelHandle, PhraseRecognitionEvent event,
+ int captureSession)
throws RemoteException {
enforcePermissions("Sound trigger phrase recognition.");
- mDelegate.onPhraseRecognition(modelHandle, event);
+ mDelegate.onPhraseRecognition(modelHandle, event, captureSession);
}
@Override
- public void onRecognitionAvailabilityChange(boolean available) throws RemoteException {
- mDelegate.onRecognitionAvailabilityChange(available);
+ public void onResourcesAvailable() throws RemoteException {
+ mDelegate.onResourcesAvailable();
+ }
+
+ @Override
+ public void onModelUnloaded(int modelHandle) throws RemoteException {
+ mDelegate.onModelUnloaded(modelHandle);
}
@Override
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
index db7a575..1995e54 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
@@ -20,7 +20,10 @@
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.soundtrigger.V2_0.ISoundTriggerHw;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.SoundModel;
import android.media.permission.ClearCallingIdentityContext;
import android.media.permission.Identity;
import android.media.permission.PermissionUtil;
@@ -28,13 +31,8 @@
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.SoundModel;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
import android.os.RemoteException;
-import android.util.Log;
import com.android.server.SystemService;
@@ -79,13 +77,6 @@
@NonNull Context context) {
mDelegate = Objects.requireNonNull(delegate);
mContext = context;
- new ExternalCaptureStateTracker(active -> {
- try {
- mDelegate.setCaptureState(active);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
- });
}
@Override
@@ -232,23 +223,15 @@
@Override
public void onStart() {
- HalFactory[] factories = new HalFactory[]{() -> {
- try {
- Log.d(TAG, "Connecting to default ISoundTriggerHw");
- return ISoundTriggerHw.getService(true);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
- }};
+ HalFactory[] factories = new HalFactory[]{new DefaultHalFactory()};
publishBinderService(Context.SOUND_TRIGGER_MIDDLEWARE_SERVICE,
- new SoundTriggerMiddlewareService(
- new SoundTriggerMiddlewareLogging(
- new SoundTriggerMiddlewarePermission(
- new SoundTriggerMiddlewareValidation(
- new SoundTriggerMiddlewareImpl(factories,
- new AudioSessionProviderImpl())),
- getContext())), getContext()));
+ new SoundTriggerMiddlewareService(new SoundTriggerMiddlewareLogging(
+ new SoundTriggerMiddlewarePermission(
+ new SoundTriggerMiddlewareValidation(
+ new SoundTriggerMiddlewareImpl(factories,
+ new AudioSessionProviderImpl())),
+ getContext())), getContext()));
}
}
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
index 95a30c7..d9fa792 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
@@ -18,21 +18,21 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Status;
import android.media.permission.Identity;
import android.media.permission.IdentityContext;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.RecognitionStatus;
-import android.media.soundtrigger_middleware.SoundModel;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
-import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
-import android.media.soundtrigger_middleware.Status;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
@@ -46,10 +46,6 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
/**
* This is a decorator of an {@link ISoundTriggerMiddlewareService}, which enforces correct usage by
@@ -89,7 +85,8 @@
* }
* </pre></code>
* Following this patterns ensures a consistent and rigorous handling of all aspects associated
- * with client-server separation.
+ * with client-server separation. Notable exceptions are stopRecognition() and unloadModel(), which
+ * follow slightly more complicated rules for synchronization (see README.md for details).
* <p>
* <b>Exception handling approach:</b><br>
* We make sure all client faults (argument and state validation) happen first, and
@@ -115,21 +112,18 @@
}
private class ModuleState {
- final @NonNull SoundTriggerModuleProperties properties;
- Set<Session> sessions = new HashSet<>();
+ public @NonNull Properties properties;
+ public Set<Session> sessions = new HashSet<>();
- private ModuleState(@NonNull SoundTriggerModuleProperties properties) {
+ private ModuleState(@NonNull Properties properties) {
this.properties = properties;
}
}
- private AtomicReference<Boolean> mCaptureState = new AtomicReference<>();
-
private final @NonNull ISoundTriggerMiddlewareInternal mDelegate;
private Map<Integer, ModuleState> mModules;
- public SoundTriggerMiddlewareValidation(
- @NonNull ISoundTriggerMiddlewareInternal delegate) {
+ public SoundTriggerMiddlewareValidation(@NonNull ISoundTriggerMiddlewareInternal delegate) {
mDelegate = delegate;
}
@@ -137,8 +131,8 @@
* Generic exception handling for exceptions thrown by the underlying implementation.
*
* Would throw any {@link RecoverableException} as a {@link ServiceSpecificException} (passed
- * by Binder to the caller) and <i>any other</i> exception as {@link InternalServerError}
- * (<b>not</b> passed by Binder to the caller).
+ * by Binder to the caller) and <i>any other</i> exception as a {@link ServiceSpecificException}
+ * with a {@link Status#INTERNAL_ERROR} code.
* <p>
* Typical usage:
* <code><pre>
@@ -149,8 +143,7 @@
* }
* </pre></code>
*/
- static @NonNull
- RuntimeException handleException(@NonNull Exception e) {
+ static @NonNull RuntimeException handleException(@NonNull Exception e) {
if (e instanceof RecoverableException) {
throw new ServiceSpecificException(((RecoverableException) e).errorCode,
e.getMessage());
@@ -161,8 +154,7 @@
}
@Override
- public @NonNull
- SoundTriggerModuleDescriptor[] listModules() {
+ public @NonNull SoundTriggerModuleDescriptor[] listModules() {
// Input validation (always valid).
synchronized (this) {
@@ -186,6 +178,7 @@
throw new RuntimeException(
"listModules must always return the same result.");
}
+ mModules.get(desc.handle).properties = desc.properties;
}
}
return result;
@@ -223,23 +216,6 @@
}
}
- @Override
- public void setCaptureState(boolean active) {
- // This is an internal call. No permissions needed.
- //
- // Normally, we would acquire a lock here. However, we do not access any state here so it
- // is safe to not lock. This call is typically done from a different context than all the
- // other calls and may result in a deadlock if we lock here (between the audio server and
- // the system server).
- try {
- mDelegate.setCaptureState(active);
- } catch (Exception e) {
- throw handleException(e);
- } finally {
- mCaptureState.set(active);
- }
- }
-
// Override toString() in order to have the delegate's ID in it.
@Override
public String toString() {
@@ -247,17 +223,8 @@
}
@Override
- public IBinder asBinder() {
- throw new UnsupportedOperationException(
- "This implementation is not inteded to be used directly with Binder.");
- }
-
- @Override
public void dump(PrintWriter pw) {
synchronized (this) {
- Boolean captureState = mCaptureState.get();
- pw.printf("Capture state is %s\n\n", captureState == null ? "uninitialized"
- : (captureState ? "active" : "inactive"));
if (mModules != null) {
for (int handle : mModules.keySet()) {
final ModuleState module = mModules.get(handle);
@@ -303,10 +270,14 @@
* delivered to the caller (most commonly, for permission reasons).
*/
INTERCEPTED,
+ /**
+ * Model has been preemptively unloaded by the HAL.
+ */
+ PREEMPTED,
}
/** Activity state. */
- private AtomicInteger mActivityState = new AtomicInteger(Activity.LOADED.ordinal());
+ Activity activityState = Activity.LOADED;
/** Human-readable description of the model. */
final String description;
@@ -316,7 +287,7 @@
* parameter is supported. A null value means it is known to not be supported. A non-null
* value indicates the valid value range.
*/
- private Map<Integer, ModelParameterRange> parameterSupport = new HashMap<>();
+ private final Map<Integer, ModelParameterRange> parameterSupport = new HashMap<>();
/**
* Check that the given parameter is known to be supported for this model.
@@ -351,24 +322,6 @@
Preconditions.checkArgumentInRange(value, range.minInclusive, range.maxInclusive,
"value");
}
-
- /**
- * Update support state for the given parameter for this model.
- *
- * @param modelParam The parameter key.
- * @param range The parameter value range, or null if not supported.
- */
- void updateParameterSupport(int modelParam, @Nullable ModelParameterRange range) {
- parameterSupport.put(modelParam, range);
- }
-
- Activity getActivityState() {
- return Activity.values()[mActivityState.get()];
- }
-
- void setActivityState(Activity activity) {
- mActivityState.set(activity.ordinal());
- }
}
/**
@@ -377,13 +330,7 @@
*/
private class Session extends ISoundTriggerModule.Stub {
private ISoundTriggerModule mDelegate;
- // While generally all the fields of this class must be changed under a lock, an exception
- // is made for the specific case of changing a model state from ACTIVE to LOADED, which
- // may happen as result of a recognition callback. This would happen atomically and is
- // necessary in order to avoid deadlocks associated with locking from within callbacks
- // possibly originating from the audio server.
- private @NonNull
- ConcurrentMap<Integer, ModelState> mLoadedModels = new ConcurrentHashMap<>();
+ private final @NonNull Map<Integer, ModelState> mLoadedModels = new HashMap<>();
private final int mHandle;
private ModuleStatus mState = ModuleStatus.ALIVE;
private final CallbackWrapper mCallbackWrapper;
@@ -451,7 +398,6 @@
@Override
public void unloadModel(int modelHandle) {
// Input validation (always valid).
-
synchronized (SoundTriggerMiddlewareValidation.this) {
// State validation.
if (mState == ModuleStatus.DETACHED) {
@@ -462,18 +408,24 @@
if (modelState == null) {
throw new IllegalStateException("Invalid handle: " + modelHandle);
}
- if (modelState.getActivityState() != ModelState.Activity.LOADED) {
+ // To avoid race conditions, we treat LOADED and PREEMPTED exactly the same.
+ if (modelState.activityState != ModelState.Activity.LOADED
+ && modelState.activityState != ModelState.Activity.PREEMPTED) {
throw new IllegalStateException("Model with handle: " + modelHandle
+ " has invalid state for unloading");
}
+ }
- // From here on, every exception isn't client's fault.
- try {
- mDelegate.unloadModel(modelHandle);
- mLoadedModels.remove(modelHandle);
- } catch (Exception e) {
- throw handleException(e);
- }
+ // From here on, every exception isn't client's fault.
+ try {
+ // Calling the delegate must be done outside the lock.
+ mDelegate.unloadModel(modelHandle);
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ mLoadedModels.remove(modelHandle);
}
}
@@ -492,20 +444,19 @@
if (modelState == null) {
throw new IllegalStateException("Invalid handle: " + modelHandle);
}
- if (modelState.getActivityState() != ModelState.Activity.LOADED) {
+ ModelState.Activity activityState = modelState.activityState;
+ // To avoid race conditions, we treat LOADED and PREEMPTED exactly the same.
+ if (activityState != ModelState.Activity.LOADED
+ && activityState != ModelState.Activity.PREEMPTED) {
throw new IllegalStateException("Model with handle: " + modelHandle
+ " has invalid state for starting recognition");
}
// From here on, every exception isn't client's fault.
try {
- // Normally, we would set the state after the operation succeeds. However, since
- // the activity state may be reset outside of the lock, we set it here first,
- // and reset it in case of exception.
- modelState.setActivityState(ModelState.Activity.ACTIVE);
mDelegate.startRecognition(modelHandle, config);
+ modelState.activityState = ModelState.Activity.ACTIVE;
} catch (Exception e) {
- modelState.setActivityState(ModelState.Activity.LOADED);
throw handleException(e);
}
}
@@ -529,17 +480,36 @@
// From here on, every exception isn't client's fault.
try {
- // If the activity state is LOADED or INTERCEPTED, we skip delegating the
- // command, but still consider the call valid. In either case, the resulting
- // state is LOADED.
- if (modelState.getActivityState() == ModelState.Activity.ACTIVE) {
- mDelegate.stopRecognition(modelHandle);
+ // If the activity state is INTERCEPTED, we skip delegating the command, but
+ // still consider the call valid.
+ if (modelState.activityState == ModelState.Activity.INTERCEPTED) {
+ modelState.activityState = ModelState.Activity.LOADED;
+ return;
}
- modelState.setActivityState(ModelState.Activity.LOADED);
} catch (Exception e) {
throw handleException(e);
}
}
+
+ // Calling the delegate's stop must be done without the lock.
+ try {
+ mDelegate.stopRecognition(modelHandle);
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ ModelState modelState = mLoadedModels.get(modelHandle);
+ if (modelState == null) {
+ // The model was unloaded while we let go of the lock.
+ return;
+ }
+
+ // After the call, the state is LOADED, unless it has been first preempted.
+ if (modelState.activityState != ModelState.Activity.PREEMPTED) {
+ modelState.activityState = ModelState.Activity.LOADED;
+ }
+ }
}
@Override
@@ -562,7 +532,7 @@
try {
// If the activity state is LOADED or INTERCEPTED, we skip delegating the
// command, but still consider the call valid.
- if (modelState.getActivityState() == ModelState.Activity.ACTIVE) {
+ if (modelState.activityState == ModelState.Activity.ACTIVE) {
mDelegate.forceRecognitionEvent(modelHandle);
}
} catch (Exception e) {
@@ -644,7 +614,7 @@
try {
ModelParameterRange result = mDelegate.queryModelParameterSupport(modelHandle,
modelParam);
- modelState.updateParameterSupport(modelParam, result);
+ modelState.parameterSupport.put(modelParam, result);
return result;
} catch (Exception e) {
throw handleException(e);
@@ -702,7 +672,7 @@
for (Map.Entry<Integer, ModelState> entry : mLoadedModels.entrySet()) {
pw.print(entry.getKey());
pw.print('\t');
- pw.print(entry.getValue().getActivityState().name());
+ pw.print(entry.getValue().activityState.name());
pw.print('\t');
pw.print(entry.getValue().description);
pw.println();
@@ -732,70 +702,79 @@
}
@Override
- public void onRecognition(int modelHandle, @NonNull RecognitionEvent event) {
- // We cannot obtain a lock on SoundTriggerMiddlewareValidation.this, since this call
- // might be coming from the audio server (via setCaptureState()) while it is holding
- // a lock that is also acquired while loading / unloading models. Thus, we require a
- // strict locking order here, where obtaining our lock must always come first.
- // To avoid this problem, we use an atomic model activity state. There is a risk of the
- // model not being in the mLoadedModels map here, since it might have been stopped /
- // unloaded while the event was in flight.
- ModelState modelState = mLoadedModels.get(modelHandle);
- if (modelState != null) {
- if (event.status != RecognitionStatus.FORCED) {
- modelState.setActivityState(ModelState.Activity.LOADED);
- }
- try {
- mCallback.onRecognition(modelHandle, event);
- } catch (Exception e) {
- // Dead client will be handled by binderDied() - no need to handle here.
- // In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback exception.", e);
+ public void onRecognition(int modelHandle, @NonNull RecognitionEvent event,
+ int captureSession) {
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ ModelState modelState = mLoadedModels.get(modelHandle);
if (event.status != RecognitionStatus.FORCED) {
- modelState.setActivityState(ModelState.Activity.INTERCEPTED);
+ modelState.activityState = ModelState.Activity.LOADED;
+ }
+ }
+
+ // Calling the delegate callback must be done outside the lock.
+ try {
+ mCallback.onRecognition(modelHandle, event, captureSession);
+ } catch (Exception e) {
+ Log.w(TAG, "Client callback exception.", e);
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ ModelState modelState = mLoadedModels.get(modelHandle);
+ if (event.status != RecognitionStatus.FORCED) {
+ modelState.activityState = ModelState.Activity.INTERCEPTED;
+ }
}
}
}
- }
- @Override
- public void onPhraseRecognition(int modelHandle, @NonNull PhraseRecognitionEvent event) {
- // We cannot obtain a lock on SoundTriggerMiddlewareValidation.this, since this call
- // might be coming from the audio server (via setCaptureState()) while it is holding
- // a lock that is also acquired while loading / unloading models. Thus, we require a
- // strict locking order here, where obtaining our lock must always come first.
- // To avoid this problem, we use an atomic model activity state. There is a risk of the
- // model not being in the mLoadedModels map here, since it might have been stopped /
- // unloaded while the event was in flight.
- ModelState modelState = mLoadedModels.get(modelHandle);
- if (modelState != null) {
- if (event.common.status != RecognitionStatus.FORCED) {
- modelState.setActivityState(ModelState.Activity.LOADED);
+ @Override
+ public void onPhraseRecognition(int modelHandle,
+ @NonNull PhraseRecognitionEvent event, int captureSession) {
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ ModelState modelState = mLoadedModels.get(modelHandle);
+ if (event.common.status != RecognitionStatus.FORCED) {
+ modelState.activityState = ModelState.Activity.LOADED;
+ }
}
+
+ // Calling the delegate callback must be done outside the lock.
try {
- mCallback.onPhraseRecognition(modelHandle, event);
+ mCallback.onPhraseRecognition(modelHandle, event, captureSession);
} catch (Exception e) {
+ Log.w(TAG, "Client callback exception.", e);
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ ModelState modelState = mLoadedModels.get(modelHandle);
+ if (event.common.status != RecognitionStatus.FORCED) {
+ modelState.activityState = ModelState.Activity.INTERCEPTED;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onModelUnloaded(int modelHandle) {
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ ModelState modelState = mLoadedModels.get(modelHandle);
+ modelState.activityState = ModelState.Activity.PREEMPTED;
+ }
+
+ // Calling the delegate callback must be done outside the lock.
+ try {
+ mCallback.onModelUnloaded(modelHandle);
+ } catch (Exception e) {
+ Log.w(TAG, "Client callback exception.", e);
+ }
+ }
+
+ @Override
+ public void onResourcesAvailable() {
+ // Not locking to avoid deadlocks (not affecting any state).
+ try {
+ mCallback.onResourcesAvailable();
+ } catch (RemoteException e) {
// Dead client will be handled by binderDied() - no need to handle here.
// In any case, client callbacks are considered best effort.
Log.e(TAG, "Client callback exception.", e);
- if (event.common.status != RecognitionStatus.FORCED) {
- modelState.setActivityState(ModelState.Activity.INTERCEPTED);
- }
}
}
- }
-
- @Override
- public void onRecognitionAvailabilityChange(boolean available) {
- // Not locking to avoid deadlocks (not affecting any state).
- try {
- mCallback.onRecognitionAvailabilityChange(available);
- } catch (RemoteException e) {
- // Dead client will be handled by binderDied() - no need to handle here.
- // In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback exception.", e);
- }
- }
@Override
public void onModuleDied() {
@@ -820,8 +799,7 @@
// Gracefully stop all active recognitions and unload the models.
for (Map.Entry<Integer, ModelState> entry :
mLoadedModels.entrySet()) {
- if (entry.getValue().getActivityState()
- == ModelState.Activity.ACTIVE) {
+ if (entry.getValue().activityState == ModelState.Activity.ACTIVE) {
mDelegate.stopRecognition(entry.getKey());
}
mDelegate.unloadModel(entry.getKey());
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
index 02d978d..f211158 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
@@ -18,30 +18,24 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback;
-import android.hardware.soundtrigger.V2_2.ISoundTriggerHw;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Status;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.RecognitionStatus;
-import android.media.soundtrigger_middleware.SoundModel;
-import android.media.soundtrigger_middleware.SoundModelType;
-import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
-import android.media.soundtrigger_middleware.Status;
import android.os.IBinder;
-import android.os.IHwBinder;
import android.os.RemoteException;
import android.util.Log;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -71,9 +65,8 @@
* <li>There is no binder instance associated with this implementation. Do not call asBinder().
* <li>The implementation may throw a {@link RecoverableException} to indicate non-fatal,
* recoverable faults. The error code would one of the
- * {@link android.media.soundtrigger_middleware.Status} constants. Any other exception
- * thrown should be regarded as a bug in the implementation or one of its dependencies
- * (assuming correct usage).
+ * {@link android.media.soundtrigger.Status} constants. Any other exception thrown should be
+ * regarded as a bug in the implementation or one of its dependencies (assuming correct usage).
* <li>The implementation is designed for testability by featuring dependency injection (the
* underlying HAL driver instances are passed to the ctor) and by minimizing dependencies
* on Android runtime.
@@ -88,15 +81,13 @@
*
* @hide
*/
-class SoundTriggerModule implements IHwBinder.DeathRecipient {
+class SoundTriggerModule implements IBinder.DeathRecipient, ISoundTriggerHal.GlobalCallback {
static private final String TAG = "SoundTriggerModule";
- @NonNull private HalFactory mHalFactory;
- @NonNull private ISoundTriggerHw2 mHalService;
+ @NonNull private final HalFactory mHalFactory;
+ @NonNull private ISoundTriggerHal mHalService;
@NonNull private final SoundTriggerMiddlewareImpl.AudioSessionProvider mAudioSessionProvider;
private final Set<Session> mActiveSessions = new HashSet<>();
- private int mNumLoadedModels = 0;
- private SoundTriggerModuleProperties mProperties;
- private boolean mRecognitionAvailable;
+ private Properties mProperties;
/**
* Ctor.
@@ -110,9 +101,6 @@
mAudioSessionProvider = audioSessionProvider;
attachToHal();
- mProperties = ConversionUtil.hidl2aidlProperties(mHalService.getProperties());
- // We conservatively assume that external capture is active until explicitly told otherwise.
- mRecognitionAvailable = mProperties.concurrentCapture;
}
/**
@@ -140,50 +128,13 @@
* @return The properties structure.
*/
synchronized @NonNull
- SoundTriggerModuleProperties getProperties() {
+ Properties getProperties() {
return mProperties;
}
- /**
- * Notify the module that external capture has started / finished, using the same input device
- * used for recognition.
- * If the underlying driver does not support recognition while capturing, capture will be
- * aborted, and the recognition callback will receive and abort event. In addition, all active
- * clients will be notified of the change in state.
- *
- * @param active true iff external capture is active.
- */
- void setExternalCaptureState(boolean active) {
- // We should never invoke callbacks while holding the lock, since this may deadlock with
- // forward calls. Thus, we first gather all the callbacks we need to invoke while holding
- // the lock, but invoke them after releasing it.
- List<Runnable> callbacks = new LinkedList<>();
-
- synchronized (this) {
- if (mProperties.concurrentCapture) {
- // If we support concurrent capture, we don't care about any of this.
- return;
- }
- mRecognitionAvailable = !active;
- if (!mRecognitionAvailable) {
- // Our module does not support recognition while a capture is active -
- // need to abort all active recognitions.
- for (Session session : mActiveSessions) {
- session.abortActiveRecognitions(callbacks);
- }
- }
- }
- for (Runnable callback : callbacks) {
- callback.run();
- }
- for (Session session : mActiveSessions) {
- session.notifyRecognitionAvailability();
- }
- }
-
@Override
- public void serviceDied(long cookie) {
- Log.w(TAG, String.format("Underlying HAL driver died."));
+ public void binderDied() {
+ Log.w(TAG, "Underlying HAL driver died.");
List<ISoundTriggerCallback> callbacks;
synchronized (this) {
callbacks = new ArrayList<>(mActiveSessions.size());
@@ -207,20 +158,19 @@
* Resets the transient state of this object.
*/
private void reset() {
+ mHalService.detach();
attachToHal();
- // We conservatively assume that external capture is active until explicitly told otherwise.
- mRecognitionAvailable = mProperties.concurrentCapture;
- mNumLoadedModels = 0;
}
/**
* Attached to the HAL service via factory.
*/
private void attachToHal() {
- mHalService = new SoundTriggerHw2Enforcer(
- new SoundTriggerHw2Watchdog(
- new SoundTriggerHw2Compat(mHalFactory.create())));
- mHalService.linkToDeath(this, 0);
+ mHalService = new SoundTriggerHalEnforcer(
+ new SoundTriggerHalWatchdog(mHalFactory.create()));
+ mHalService.linkToDeath(this);
+ mHalService.registerCallback(this);
+ mProperties = mHalService.getProperties();
}
/**
@@ -232,6 +182,25 @@
mActiveSessions.remove(session);
}
+ @Override
+ public void onResourcesAvailable() {
+ List<ISoundTriggerCallback> callbacks;
+ synchronized (this) {
+ callbacks = new ArrayList<>(mActiveSessions.size());
+ for (Session session : mActiveSessions) {
+ callbacks.add(session.mCallback);
+ }
+ }
+ // Trigger the callbacks outside of the lock to avoid deadlocks.
+ for (ISoundTriggerCallback callback : callbacks) {
+ try {
+ callback.onResourcesAvailable();
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+ }
+
/** State of a single sound model. */
private enum ModelState {
/** Initial state, until load() is called. */
@@ -249,7 +218,7 @@
*/
private class Session implements ISoundTriggerModule {
private ISoundTriggerCallback mCallback;
- private Map<Integer, Model> mLoadedModels = new HashMap<>();
+ private final Map<Integer, Model> mLoadedModels = new HashMap<>();
/**
* Ctor.
@@ -258,7 +227,6 @@
*/
private Session(@NonNull ISoundTriggerCallback callback) {
mCallback = callback;
- notifyRecognitionAvailability();
}
@Override
@@ -274,95 +242,65 @@
@Override
public int loadModel(@NonNull SoundModel model) {
- // We must do this outside the lock, to avoid possible deadlocks with the remote process
- // that provides the audio sessions, which may also be calling into us.
- SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession =
- mAudioSessionProvider.acquireSession();
-
- try {
- synchronized (SoundTriggerModule.this) {
- checkValid();
- if (mNumLoadedModels == mProperties.maxSoundModels) {
- throw new RecoverableException(Status.RESOURCE_CONTENTION,
- "Maximum number of models loaded.");
- }
- Model loadedModel = new Model();
- int result = loadedModel.load(model, audioSession);
- ++mNumLoadedModels;
- return result;
- }
- } catch (Exception e) {
- // We must do this outside the lock, to avoid possible deadlocks with the remote
- // process that provides the audio sessions, which may also be calling into us.
+ synchronized (SoundTriggerModule.this) {
+ SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession =
+ mAudioSessionProvider.acquireSession();
try {
- mAudioSessionProvider.releaseSession(audioSession.mSessionHandle);
- } catch (Exception ee) {
- Log.e(TAG, "Failed to release session.", ee);
+ checkValid();
+ Model loadedModel = new Model();
+ return loadedModel.load(model, audioSession);
+ } catch (Exception e) {
+ // We must do this outside the lock, to avoid possible deadlocks with the remote
+ // process that provides the audio sessions, which may also be calling into us.
+ try {
+ mAudioSessionProvider.releaseSession(audioSession.mSessionHandle);
+ } catch (Exception ee) {
+ Log.e(TAG, "Failed to release session.", ee);
+ }
+ throw e;
}
- throw e;
}
}
@Override
public int loadPhraseModel(@NonNull PhraseSoundModel model) {
- // We must do this outside the lock, to avoid possible deadlocks with the remote process
- // that provides the audio sessions, which may also be calling into us.
- SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession =
- mAudioSessionProvider.acquireSession();
-
- try {
- synchronized (SoundTriggerModule.this) {
+ synchronized (SoundTriggerModule.this) {
+ SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession =
+ mAudioSessionProvider.acquireSession();
+ try {
checkValid();
- if (mNumLoadedModels == mProperties.maxSoundModels) {
- throw new RecoverableException(Status.RESOURCE_CONTENTION,
- "Maximum number of models loaded.");
- }
Model loadedModel = new Model();
int result = loadedModel.load(model, audioSession);
- ++mNumLoadedModels;
Log.d(TAG, String.format("loadPhraseModel()->%d", result));
return result;
+ } catch (Exception e) {
+ // We must do this outside the lock, to avoid possible deadlocks with the remote
+ // process that provides the audio sessions, which may also be calling into us.
+ try {
+ mAudioSessionProvider.releaseSession(audioSession.mSessionHandle);
+ } catch (Exception ee) {
+ Log.e(TAG, "Failed to release session.", ee);
+ }
+ throw e;
}
- } catch (Exception e) {
- // We must do this outside the lock, to avoid possible deadlocks with the remote
- // process that provides the audio sessions, which may also be calling into us.
- try {
- mAudioSessionProvider.releaseSession(audioSession.mSessionHandle);
- } catch (Exception ee) {
- Log.e(TAG, "Failed to release session.", ee);
- }
- throw e;
}
}
@Override
public void unloadModel(int modelHandle) {
- int sessionId;
synchronized (SoundTriggerModule.this) {
+ int sessionId;
checkValid();
sessionId = mLoadedModels.get(modelHandle).unload();
- --mNumLoadedModels;
+ mAudioSessionProvider.releaseSession(sessionId);
}
-
- // We must do this outside the lock, to avoid possible deadlocks with the remote process
- // that provides the audio sessions, which may also be calling into us.
- mAudioSessionProvider.releaseSession(sessionId);
}
@Override
public void startRecognition(int modelHandle, @NonNull RecognitionConfig config) {
- // We should never invoke callbacks while holding the lock, since this may deadlock with
- // forward calls. Thus, we first gather all the callbacks we need to invoke while holding
- // the lock, but invoke them after releasing it.
- List<Runnable> callbacks = new LinkedList<>();
-
synchronized (SoundTriggerModule.this) {
checkValid();
- mLoadedModels.get(modelHandle).startRecognition(config, callbacks);
- }
-
- for (Runnable callback : callbacks) {
- callback.run();
+ mLoadedModels.get(modelHandle).startRecognition(config);
}
}
@@ -407,27 +345,6 @@
}
/**
- * Abort all currently active recognitions.
- * @param callbacks Will be appended with a list of callbacks that need to be invoked
- * after this method returns, without holding the module lock.
- */
- private void abortActiveRecognitions(@NonNull List<Runnable> callbacks) {
- for (Model model : mLoadedModels.values()) {
- model.abortActiveRecognition(callbacks);
- }
- }
-
- private void notifyRecognitionAvailability() {
- try {
- mCallback.onRecognitionAvailabilityChange(mRecognitionAvailable);
- } catch (RemoteException e) {
- // Dead client will be handled by binderDied() - no need to handle here.
- // In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback execption.", e);
- }
- }
-
- /**
* The underlying module HAL is dead.
* @return The client callback that needs to be invoked to notify the client.
*/
@@ -455,10 +372,9 @@
*
* All model-based operations are delegated to this class and implemented here.
*/
- private class Model implements ISoundTriggerHw2.Callback {
+ private class Model implements ISoundTriggerHal.ModelCallback {
public int mHandle;
private ModelState mState = ModelState.INIT;
- private int mModelType = SoundModelType.UNKNOWN;
private SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession mSession;
private @NonNull
@@ -473,11 +389,8 @@
private int load(@NonNull SoundModel model,
SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession) {
- mModelType = model.type;
mSession = audioSession;
- ISoundTriggerHw.SoundModel hidlModel = ConversionUtil.aidl2hidlSoundModel(model);
-
- mHandle = mHalService.loadSoundModel(hidlModel, this, 0);
+ mHandle = mHalService.loadSoundModel(model, this);
setState(ModelState.LOADED);
mLoadedModels.put(mHandle, this);
return mHandle;
@@ -485,12 +398,8 @@
private int load(@NonNull PhraseSoundModel model,
SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession) {
- mModelType = model.common.type;
mSession = audioSession;
- ISoundTriggerHw.PhraseSoundModel hidlModel =
- ConversionUtil.aidl2hidlPhraseSoundModel(model);
-
- mHandle = mHalService.loadPhraseSoundModel(hidlModel, this, 0);
+ mHandle = mHalService.loadPhraseSoundModel(model, this);
setState(ModelState.LOADED);
mLoadedModels.put(mHandle, this);
@@ -507,18 +416,9 @@
return mSession.mSessionHandle;
}
- private void startRecognition(@NonNull RecognitionConfig config,
- @NonNull List<Runnable> callbacks) {
- if (!mRecognitionAvailable) {
- // Recognition is unavailable - send an abort event immediately.
- callbacks.add(this::notifyAbort);
- return;
- }
- android.hardware.soundtrigger.V2_3.RecognitionConfig hidlConfig =
- ConversionUtil.aidl2hidlRecognitionConfig(config);
- hidlConfig.base.header.captureDevice = mSession.mDeviceHandle;
- hidlConfig.base.header.captureHandle = mSession.mIoHandle;
- mHalService.startRecognition(mHandle, hidlConfig, this, 0);
+ private void startRecognition(@NonNull RecognitionConfig config) {
+ mHalService.startRecognition(mHandle, mSession.mDeviceHandle,
+ mSession.mIoHandle, config);
setState(ModelState.ACTIVE);
}
@@ -537,7 +437,7 @@
// This call is idempotent in order to avoid races.
return;
}
- mHalService.getModelState(mHandle);
+ mHalService.forceRecognitionEvent(mHandle);
}
@@ -553,78 +453,24 @@
@Nullable
private ModelParameterRange queryModelParameterSupport(int modelParam) {
- return ConversionUtil.hidl2aidlModelParameterRange(
- mHalService.queryParameter(mHandle,
- ConversionUtil.aidl2hidlModelParameter(modelParam)));
- }
-
- /**
- * Abort the recognition, if active.
- * @param callbacks Will be appended with a list of callbacks that need to be invoked
- * after this method returns, without holding the module lock.
- */
- private void abortActiveRecognition(List<Runnable> callbacks) {
- // If we're inactive, do nothing.
- if (getState() != ModelState.ACTIVE) {
- return;
- }
- // Stop recognition.
- stopRecognition();
-
- // Notify the client that recognition has been aborted.
- callbacks.add(this::notifyAbort);
- }
-
- /** Notify the client that recognition has been aborted. */
- private void notifyAbort() {
- try {
- switch (mModelType) {
- case SoundModelType.GENERIC: {
- android.media.soundtrigger_middleware.RecognitionEvent event =
- newEmptyRecognitionEvent();
- event.status =
- android.media.soundtrigger_middleware.RecognitionStatus.ABORTED;
- event.type = SoundModelType.GENERIC;
- mCallback.onRecognition(mHandle, event);
- }
- break;
-
- case SoundModelType.KEYPHRASE: {
- android.media.soundtrigger_middleware.PhraseRecognitionEvent event =
- newEmptyPhraseRecognitionEvent();
- event.common.status =
- android.media.soundtrigger_middleware.RecognitionStatus.ABORTED;
- event.common.type = SoundModelType.KEYPHRASE;
- mCallback.onPhraseRecognition(mHandle, event);
- }
- break;
-
- default:
- Log.e(TAG, "Unknown model type: " + mModelType);
-
- }
- } catch (RemoteException e) {
- // Dead client will be handled by binderDied() - no need to handle here.
- // In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback execption.", e);
- }
+ return mHalService.queryParameter(mHandle, modelParam);
}
@Override
- public void recognitionCallback(
- @NonNull ISoundTriggerHwCallback.RecognitionEvent recognitionEvent,
- int cookie) {
- RecognitionEvent aidlEvent =
- ConversionUtil.hidl2aidlRecognitionEvent(recognitionEvent);
- aidlEvent.captureSession = mSession.mSessionHandle;
+ public void recognitionCallback(int modelHandle,
+ @NonNull RecognitionEvent recognitionEvent) {
+ ISoundTriggerCallback callback;
synchronized (SoundTriggerModule.this) {
- if (aidlEvent.status != RecognitionStatus.FORCED) {
+ if (recognitionEvent.status != RecognitionStatus.FORCED) {
setState(ModelState.LOADED);
}
+ callback = mCallback;
}
// The callback must be invoked outside of the lock.
try {
- mCallback.onRecognition(mHandle, aidlEvent);
+ if (callback != null) {
+ callback.onRecognition(mHandle, recognitionEvent, mSession.mSessionHandle);
+ }
} catch (RemoteException e) {
// We're not expecting any exceptions here.
throw e.rethrowAsRuntimeException();
@@ -632,22 +478,40 @@
}
@Override
- public void phraseRecognitionCallback(
- @NonNull ISoundTriggerHwCallback.PhraseRecognitionEvent phraseRecognitionEvent,
- int cookie) {
- PhraseRecognitionEvent aidlEvent =
- ConversionUtil.hidl2aidlPhraseRecognitionEvent(phraseRecognitionEvent);
- aidlEvent.common.captureSession = mSession.mSessionHandle;
-
+ public void phraseRecognitionCallback(int modelHandle,
+ @NonNull PhraseRecognitionEvent phraseRecognitionEvent) {
+ ISoundTriggerCallback callback;
synchronized (SoundTriggerModule.this) {
- if (aidlEvent.common.status != RecognitionStatus.FORCED) {
+ if (phraseRecognitionEvent.common.status != RecognitionStatus.FORCED) {
setState(ModelState.LOADED);
}
+ callback = mCallback;
}
// The callback must be invoked outside of the lock.
try {
- mCallback.onPhraseRecognition(mHandle, aidlEvent);
+ if (callback != null) {
+ mCallback.onPhraseRecognition(mHandle, phraseRecognitionEvent,
+ mSession.mSessionHandle);
+ }
+ } catch (RemoteException e) {
+ // We're not expecting any exceptions here.
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void modelUnloaded(int modelHandle) {
+ ISoundTriggerCallback callback;
+ synchronized (SoundTriggerModule.this) {
+ callback = mCallback;
+ }
+
+ // The callback must be invoked outside of the lock.
+ try {
+ if (callback != null) {
+ callback.onModelUnloaded(modelHandle);
+ }
} catch (RemoteException e) {
// We're not expecting any exceptions here.
throw e.rethrowAsRuntimeException();
@@ -655,33 +519,4 @@
}
}
}
-
- /**
- * Creates a default-initialized recognition event.
- *
- * Non-nullable object fields are default constructed.
- * Non-nullable array fields are initialized to 0 length.
- *
- * @return The event.
- */
- private static RecognitionEvent newEmptyRecognitionEvent() {
- RecognitionEvent result = new RecognitionEvent();
- result.data = new byte[0];
- return result;
- }
-
- /**
- * Creates a default-initialized phrase recognition event.
- *
- * Non-nullable object fields are default constructed.
- * Non-nullable array fields are initialized to 0 length.
- *
- * @return The event.
- */
- private static PhraseRecognitionEvent newEmptyPhraseRecognitionEvent() {
- PhraseRecognitionEvent result = new PhraseRecognitionEvent();
- result.common = newEmptyRecognitionEvent();
- result.phraseExtras = new PhraseRecognitionExtra[0];
- return result;
- }
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ValidationUtil.java b/services/core/java/com/android/server/soundtrigger_middleware/ValidationUtil.java
index e05c468..4d52121 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ValidationUtil.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ValidationUtil.java
@@ -17,21 +17,18 @@
package com.android.server.soundtrigger_middleware;
import android.annotation.Nullable;
-import android.media.soundtrigger_middleware.ConfidenceLevel;
-import android.media.soundtrigger_middleware.ModelParameter;
-import android.media.soundtrigger_middleware.Phrase;
-import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.RecognitionMode;
-import android.media.soundtrigger_middleware.SoundModel;
-import android.media.soundtrigger_middleware.SoundModelType;
-
-import com.android.internal.util.Preconditions;
+import android.media.soundtrigger.ConfidenceLevel;
+import android.media.soundtrigger.ModelParameter;
+import android.media.soundtrigger.Phrase;
+import android.media.soundtrigger.PhraseRecognitionExtra;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionMode;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.SoundModelType;
import java.util.Objects;
import java.util.regex.Matcher;
-import java.util.regex.Pattern;
/**
* Utilities for asserting the validity of various data types used by this module.
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index 357c232..e751a7b 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -42,6 +42,7 @@
import com.android.server.timezonedetector.ConfigurationChangeListener;
import com.android.server.timezonedetector.ReferenceWithHistory;
+import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.Objects;
@@ -321,12 +322,16 @@
ipw.println("mLastAutoSystemClockTimeSet=" + mLastAutoSystemClockTimeSet);
ipw.println("mEnvironment.isAutoTimeDetectionEnabled()="
+ mEnvironment.isAutoTimeDetectionEnabled());
- ipw.println("mEnvironment.elapsedRealtimeMillis()=" + mEnvironment.elapsedRealtimeMillis());
- ipw.println("mEnvironment.systemClockMillis()=" + mEnvironment.systemClockMillis());
+ long elapsedRealtimeMillis = mEnvironment.elapsedRealtimeMillis();
+ ipw.printf("mEnvironment.elapsedRealtimeMillis()=%s (%s)\n",
+ Duration.ofMillis(elapsedRealtimeMillis), elapsedRealtimeMillis);
+ long systemClockMillis = mEnvironment.systemClockMillis();
+ ipw.printf("mEnvironment.systemClockMillis()=%s (%s)\n",
+ Instant.ofEpochMilli(systemClockMillis), systemClockMillis);
ipw.println("mEnvironment.systemClockUpdateThresholdMillis()="
+ mEnvironment.systemClockUpdateThresholdMillis());
Instant autoTimeLowerBound = mEnvironment.autoTimeLowerBound();
- ipw.printf("mEnvironment.autoTimeLowerBound()=%s(%s)\n",
+ ipw.printf("mEnvironment.autoTimeLowerBound()=%s (%s)\n",
autoTimeLowerBound, autoTimeLowerBound.toEpochMilli());
String priorities =
Arrays.stream(mEnvironment.autoOriginPriorities())
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
index 5723e1d..ee30fa2 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
@@ -50,8 +50,6 @@
*/
private final int mProcessId;
- private boolean mIsForeground;
-
/**
* All the clients that share the same resource would be under the same group id.
*
@@ -90,6 +88,12 @@
private int mUsingCiCamId = INVALID_RESOURCE_ID;
/**
+ * If the priority is overwritten through
+ * {@link TunerResourceManagerService#setPriority(int, int)}.
+ */
+ private boolean mIsPriorityOverwritten = false;
+
+ /**
* Optional arbitrary priority value given by the client.
*
* <p>This value can override the default priorotiy calculated from
@@ -121,17 +125,10 @@
}
/**
- * Set the current isForeground status.
+ * If the client priority is overwrttien.
*/
- public void setForeground(boolean isForeground) {
- mIsForeground = isForeground;
- }
-
- /**
- * Get the previous recorded isForeground status.
- */
- public boolean isForeground() {
- return mIsForeground;
+ public boolean isPriorityOverwritten() {
+ return mIsPriorityOverwritten;
}
public int getGroupId() {
@@ -153,6 +150,17 @@
mPriority = priority;
}
+ /**
+ * Overwrite the client priority.
+ */
+ public void overwritePriority(int priority) {
+ if (priority < 0) {
+ return;
+ }
+ mIsPriorityOverwritten = true;
+ mPriority = priority;
+ }
+
public void setNiceValue(int niceValue) {
mNiceValue = niceValue;
}
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index 988582d..0c04b07 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -507,9 +507,8 @@
.useCase(profile.useCase)
.processId(pid)
.build();
- clientProfile.setForeground(checkIsForeground(pid));
clientProfile.setPriority(
- getClientPriority(profile.useCase, clientProfile.isForeground()));
+ getClientPriority(profile.useCase, checkIsForeground(pid)));
addClientProfile(clientId[0], clientProfile, listener);
}
@@ -547,8 +546,7 @@
return false;
}
- profile.setForeground(checkIsForeground(profile.getProcessId()));
- profile.setPriority(priority);
+ profile.overwritePriority(priority);
profile.setNiceValue(niceValue);
return true;
@@ -694,7 +692,7 @@
} else if (grantingFrontendHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
// Record the frontend id with the lowest client priority among all the
// in use frontends when no available frontend has been found.
- int priority = getOwnerClientPriority(fr.getOwnerClientId());
+ int priority = updateAndGetOwnerClientPriority(fr.getOwnerClientId());
if (currentLowestPriority > priority) {
inUseLowestPriorityFrHandle = fr.getHandle();
currentLowestPriority = priority;
@@ -760,7 +758,7 @@
} else {
// Record the lnb id with the lowest client priority among all the
// in use lnb when no available lnb has been found.
- int priority = getOwnerClientPriority(lnb.getOwnerClientId());
+ int priority = updateAndGetOwnerClientPriority(lnb.getOwnerClientId());
if (currentLowestPriority > priority) {
inUseLowestPriorityLnbHandle = lnb.getHandle();
currentLowestPriority = priority;
@@ -818,7 +816,7 @@
}
for (int ownerId : cas.getOwnerClientIds()) {
// Record the client id with lowest priority that is using the current Cas system.
- int priority = getOwnerClientPriority(ownerId);
+ int priority = updateAndGetOwnerClientPriority(ownerId);
if (currentLowestPriority > priority) {
lowestPriorityOwnerId = ownerId;
currentLowestPriority = priority;
@@ -867,7 +865,7 @@
}
for (int ownerId : ciCam.getOwnerClientIds()) {
// Record the client id with lowest priority that is using the current Cas system.
- int priority = getOwnerClientPriority(ownerId);
+ int priority = updateAndGetOwnerClientPriority(ownerId);
if (currentLowestPriority > priority) {
lowestPriorityOwnerId = ownerId;
currentLowestPriority = priority;
@@ -966,18 +964,17 @@
}
@VisibleForTesting
- // This mothod is to sync up the request client's foreground/background status and update
- // the client priority accordingly whenever new resource request comes in.
- protected void clientPriorityUpdateOnRequest(ClientProfile requestProfile) {
- int pid = requestProfile.getProcessId();
- boolean currentIsForeground = checkIsForeground(pid);
- if (requestProfile.isForeground() == currentIsForeground) {
+ // This mothod is to sync up the request/holder client's foreground/background status and update
+ // the client priority accordingly whenever a new resource request comes in.
+ protected void clientPriorityUpdateOnRequest(ClientProfile profile) {
+ if (profile.isPriorityOverwritten()) {
// To avoid overriding the priority set through updateClientPriority API.
return;
}
- requestProfile.setForeground(currentIsForeground);
- requestProfile.setPriority(
- getClientPriority(requestProfile.getUseCase(), currentIsForeground));
+ int pid = profile.getProcessId();
+ boolean currentIsForeground = checkIsForeground(pid);
+ profile.setPriority(
+ getClientPriority(profile.getUseCase(), currentIsForeground));
}
@VisibleForTesting
@@ -1154,13 +1151,15 @@
}
/**
- * Get the owner client's priority.
+ * Update and get the owner client's priority.
*
* @param clientId the owner client id.
* @return the priority of the owner client.
*/
- private int getOwnerClientPriority(int clientId) {
- return getClientProfile(clientId).getPriority();
+ private int updateAndGetOwnerClientPriority(int clientId) {
+ ClientProfile profile = getClientProfile(clientId);
+ clientPriorityUpdateOnRequest(profile);
+ return profile.getPriority();
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/utils/OWNERS b/services/core/java/com/android/server/utils/OWNERS
index cc41f61..62afcc8 100644
--- a/services/core/java/com/android/server/utils/OWNERS
+++ b/services/core/java/com/android/server/utils/OWNERS
@@ -1,7 +1,7 @@
per-file Snappable.java = file:/services/core/java/com/android/server/pm/OWNERS
per-file Snappable.java = shombert@google.com
-per-file SnapShot* = file:/services/core/java/com/android/server/pm/OWNERS
-per-file SnapShot* = shombert@google.com
+per-file Snapshot* = file:/services/core/java/com/android/server/pm/OWNERS
+per-file Snapshot* = shombert@google.com
per-file Watchable* = file:/services/core/java/com/android/server/pm/OWNERS
per-file Watchable* = shombert@google.com
per-file Watched* = file:/services/core/java/com/android/server/pm/OWNERS
diff --git a/services/core/java/com/android/server/vcn/OWNERS b/services/core/java/com/android/server/vcn/OWNERS
index 33b9f0f..2441e77 100644
--- a/services/core/java/com/android/server/vcn/OWNERS
+++ b/services/core/java/com/android/server/vcn/OWNERS
@@ -3,5 +3,5 @@
benedictwong@google.com
ckesting@google.com
evitayan@google.com
+junyin@google.com
nharold@google.com
-jchalard@google.com
\ No newline at end of file
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 71e31c3..6d46877 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_MAGNIFICATION_CALLBACK;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK;
import static android.os.Build.IS_USER;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
@@ -34,6 +36,7 @@
import static com.android.server.accessibility.AccessibilityTraceProto.CALLING_PKG;
import static com.android.server.accessibility.AccessibilityTraceProto.CALLING_STACKS;
import static com.android.server.accessibility.AccessibilityTraceProto.ELAPSED_REALTIME_NANOS;
+import static com.android.server.accessibility.AccessibilityTraceProto.LOGGING_TYPE;
import static com.android.server.accessibility.AccessibilityTraceProto.PROCESS_NAME;
import static com.android.server.accessibility.AccessibilityTraceProto.THREAD_ID_NAME;
import static com.android.server.accessibility.AccessibilityTraceProto.WHERE;
@@ -42,6 +45,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.utils.RegionUtils.forEachRect;
+import android.accessibilityservice.AccessibilityTrace;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
@@ -90,6 +94,7 @@
import com.android.internal.R;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.TraceBuffer;
+import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.wm.WindowManagerInternal.AccessibilityControllerInternal;
@@ -99,8 +104,6 @@
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
-import java.nio.file.Files;
-import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
@@ -116,21 +119,16 @@
private static final String TAG = AccessibilityController.class.getSimpleName();
private static final Object STATIC_LOCK = new Object();
- static AccessibilityControllerInternal
+ static AccessibilityControllerInternalImpl
getAccessibilityControllerInternal(WindowManagerService service) {
return AccessibilityControllerInternalImpl.getInstance(service);
}
- private final AccessibilityTracing mAccessibilityTracing;
+ private final AccessibilityControllerInternalImpl mAccessibilityTracing;
private final WindowManagerService mService;
private static final Rect EMPTY_RECT = new Rect();
private static final float[] sTempFloats = new float[9];
- AccessibilityController(WindowManagerService service) {
- mService = service;
- mAccessibilityTracing = AccessibilityTracing.getInstance(service);
- }
-
private SparseArray<DisplayMagnifier> mDisplayMagnifiers = new SparseArray<>();
private SparseArray<WindowsForAccessibilityObserver> mWindowsForAccessibilityObserver =
new SparseArray<>();
@@ -138,10 +136,17 @@
// Set to true if initializing window population complete.
private boolean mAllObserversInitialized = true;
+ AccessibilityController(WindowManagerService service) {
+ mService = service;
+ mAccessibilityTracing =
+ AccessibilityController.getAccessibilityControllerInternal(service);
+ }
+
boolean setMagnificationCallbacks(int displayId, MagnificationCallbacks callbacks) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(
TAG + ".setMagnificationCallbacks",
+ FLAGS_MAGNIFICATION_CALLBACK,
"displayId=" + displayId + "; callbacks={" + callbacks + "}");
}
boolean result = false;
@@ -172,25 +177,31 @@
/**
* Sets a callback for observing which windows are touchable for the purposes
- * of accessibility on specified display.
+ * of accessibility on specified display. When a display is reparented and becomes
+ * an embedded one, the {@link WindowsForAccessibilityCallback#onDisplayReparented(int)}
+ * will notify the accessibility framework to remove the un-used window observer of
+ * this embedded display.
*
* @param displayId The logical display id.
* @param callback The callback.
- * @return {@code false} if display id is not valid or an embedded display.
+ * @return {@code false} if display id is not valid or an embedded display when the callback
+ * isn't null.
*/
boolean setWindowsForAccessibilityCallback(int displayId,
WindowsForAccessibilityCallback callback) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(
TAG + ".setWindowsForAccessibilityCallback",
+ FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
"displayId=" + displayId + "; callback={" + callback + "}");
}
- final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId);
- if (dc == null) {
- return false;
- }
if (callback != null) {
+ final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId);
+ if (dc == null) {
+ return false;
+ }
+
WindowsForAccessibilityObserver observer =
mWindowsForAccessibilityObserver.get(displayId);
if (isEmbeddedDisplay(dc)) {
@@ -209,21 +220,13 @@
if (Build.IS_DEBUGGABLE) {
throw new IllegalStateException(errorMessage);
}
- removeObserverOfEmbeddedDisplay(observer);
+ removeObserversForEmbeddedChildDisplays(observer);
mWindowsForAccessibilityObserver.remove(displayId);
}
observer = new WindowsForAccessibilityObserver(mService, displayId, callback);
mWindowsForAccessibilityObserver.put(displayId, observer);
mAllObserversInitialized &= observer.mInitialized;
} else {
- if (isEmbeddedDisplay(dc)) {
- // If this display is an embedded one, its window observer should be removed along
- // with the window observer of its parent display removed because the window
- // observer of the embedded display and its parent display is the same, and would
- // be removed together when stopping the window tracking of its parent display. So
- // here don't need to do removing window observer of the embedded display again.
- return true;
- }
final WindowsForAccessibilityObserver windowsForA11yObserver =
mWindowsForAccessibilityObserver.get(displayId);
if (windowsForA11yObserver == null) {
@@ -234,16 +237,17 @@
throw new IllegalStateException(errorMessage);
}
}
- removeObserverOfEmbeddedDisplay(windowsForA11yObserver);
+ removeObserversForEmbeddedChildDisplays(windowsForA11yObserver);
mWindowsForAccessibilityObserver.remove(displayId);
}
return true;
}
void performComputeChangedWindowsNot(int displayId, boolean forceSend) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(
TAG + ".performComputeChangedWindowsNot",
+ FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
"displayId=" + displayId + "; forceSend=" + forceSend);
}
WindowsForAccessibilityObserver observer = null;
@@ -260,8 +264,10 @@
}
void setMagnificationSpec(int displayId, MagnificationSpec spec) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(TAG + ".setMagnificationSpec",
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
+ | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".setMagnificationSpec",
+ FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
"displayId=" + displayId + "; spec={" + spec + "}");
}
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
@@ -276,8 +282,9 @@
}
void getMagnificationRegion(int displayId, Region outMagnificationRegion) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(TAG + ".getMagnificationRegion",
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".getMagnificationRegion",
+ FLAGS_MAGNIFICATION_CALLBACK,
"displayId=" + displayId + "; outMagnificationRegion={" + outMagnificationRegion
+ "}");
}
@@ -288,9 +295,10 @@
}
void onRectangleOnScreenRequested(int displayId, Rect rectangle) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(
TAG + ".onRectangleOnScreenRequested",
+ FLAGS_MAGNIFICATION_CALLBACK,
"displayId=" + displayId + "; rectangle={" + rectangle + "}");
}
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
@@ -301,9 +309,11 @@
}
void onWindowLayersChanged(int displayId) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
- TAG + ".onWindowLayersChanged", "displayId=" + displayId);
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
+ | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".onWindowLayersChanged",
+ FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
+ "displayId=" + displayId);
}
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
@@ -317,8 +327,10 @@
}
void onRotationChanged(DisplayContent displayContent) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(TAG + ".onRotationChanged",
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
+ | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".onRotationChanged",
+ FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
"displayContent={" + displayContent + "}");
}
final int displayId = displayContent.getDisplayId();
@@ -334,8 +346,9 @@
}
void onAppWindowTransition(int displayId, int transition) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(TAG + ".onAppWindowTransition",
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".onAppWindowTransition",
+ FLAGS_MAGNIFICATION_CALLBACK,
"displayId=" + displayId + "; transition=" + transition);
}
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
@@ -346,8 +359,10 @@
}
void onWindowTransition(WindowState windowState, int transition) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(TAG + ".onWindowTransition",
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
+ | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".onWindowTransition",
+ FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
"windowState={" + windowState + "}; transition=" + transition);
}
final int displayId = windowState.getDisplayId();
@@ -364,9 +379,9 @@
void onWindowFocusChangedNot(int displayId) {
// Not relevant for the display magnifier.
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
- TAG + ".onWindowFocusChangedNot", "displayId=" + displayId);
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".onWindowFocusChangedNot",
+ FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK, "displayId=" + displayId);
}
WindowsForAccessibilityObserver observer = null;
synchronized (mService.mGlobalLock) {
@@ -426,12 +441,10 @@
}
void onSomeWindowResizedOrMovedWithCallingUid(int callingUid, int... displayIds) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
- TAG + ".onSomeWindowResizedOrMoved",
- "displayIds={" + displayIds.toString() + "}",
- "".getBytes(),
- callingUid);
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".onSomeWindowResizedOrMoved",
+ FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
+ "displayIds={" + displayIds.toString() + "}", "".getBytes(), callingUid);
}
// Not relevant for the display magnifier.
for (int i = 0; i < displayIds.length; i++) {
@@ -444,9 +457,10 @@
}
void drawMagnifiedRegionBorderIfNeeded(int displayId, SurfaceControl.Transaction t) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(
TAG + ".drawMagnifiedRegionBorderIfNeeded",
+ FLAGS_MAGNIFICATION_CALLBACK,
"displayId=" + displayId + "; transaction={" + t + "}");
}
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
@@ -457,8 +471,9 @@
}
MagnificationSpec getMagnificationSpecForWindow(WindowState windowState) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(TAG + ".getMagnificationSpecForWindow",
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".getMagnificationSpecForWindow",
+ FLAGS_MAGNIFICATION_CALLBACK,
"windowState={" + windowState + "}");
}
final int displayId = windowState.getDisplayId();
@@ -470,17 +485,19 @@
}
boolean hasCallbacks() {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(TAG + ".hasCallbacks");
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
+ | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".hasCallbacks",
+ FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK);
}
return (mDisplayMagnifiers.size() > 0
|| mWindowsForAccessibilityObserver.size() > 0);
}
void setForceShowMagnifiableBounds(int displayId, boolean show) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(TAG + ".setForceShowMagnifiableBounds",
- "displayId=" + displayId + "; show=" + show);
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".setForceShowMagnifiableBounds",
+ FLAGS_MAGNIFICATION_CALLBACK, "displayId=" + displayId + "; show=" + show);
}
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
@@ -497,39 +514,50 @@
void handleWindowObserverOfEmbeddedDisplay(
int embeddedDisplayId, WindowState parentWindow, int callingUid) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(TAG + ".handleWindowObserverOfEmbeddedDisplay",
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".handleWindowObserverOfEmbeddedDisplay",
+ FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
"embeddedDisplayId=" + embeddedDisplayId + "; parentWindowState={"
- + parentWindow + "}",
- "".getBytes(),
- callingUid);
+ + parentWindow + "}", "".getBytes(), callingUid);
}
if (embeddedDisplayId == Display.DEFAULT_DISPLAY || parentWindow == null) {
return;
}
- // Finds the parent display of this embedded display
- final int parentDisplayId;
- WindowState candidate = parentWindow;
- while (candidate != null) {
- parentWindow = candidate;
- candidate = parentWindow.getDisplayContent().getParentWindow();
+ mService.mH.sendMessage(PooledLambda.obtainMessage(
+ AccessibilityController::updateWindowObserverOfEmbeddedDisplay,
+ this, embeddedDisplayId, parentWindow));
+ }
+
+ private void updateWindowObserverOfEmbeddedDisplay(int embeddedDisplayId,
+ WindowState parentWindow) {
+ final WindowsForAccessibilityObserver windowsForA11yObserver;
+
+ synchronized (mService.mGlobalLock) {
+ // Finds the parent display of this embedded display
+ WindowState candidate = parentWindow;
+ while (candidate != null) {
+ parentWindow = candidate;
+ candidate = parentWindow.getDisplayContent().getParentWindow();
+ }
+ final int parentDisplayId = parentWindow.getDisplayId();
+ // Uses the observer of parent display
+ windowsForA11yObserver = mWindowsForAccessibilityObserver.get(parentDisplayId);
}
- parentDisplayId = parentWindow.getDisplayId();
- // Uses the observer of parent display
- final WindowsForAccessibilityObserver windowsForA11yObserver =
- mWindowsForAccessibilityObserver.get(parentDisplayId);
if (windowsForA11yObserver != null) {
+ windowsForA11yObserver.notifyDisplayReparented(embeddedDisplayId);
windowsForA11yObserver.addEmbeddedDisplay(embeddedDisplayId);
- // Replaces the observer of embedded display to the one of parent display
- mWindowsForAccessibilityObserver.put(embeddedDisplayId, windowsForA11yObserver);
+ synchronized (mService.mGlobalLock) {
+ // Replaces the observer of embedded display to the one of parent display
+ mWindowsForAccessibilityObserver.put(embeddedDisplayId, windowsForA11yObserver);
+ }
}
}
void onImeSurfaceShownChanged(WindowState windowState, boolean shown) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(TAG + ".onImeSurfaceShownChanged",
- "windowState=" + windowState + "; shown=" + shown);
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".onImeSurfaceShownChanged",
+ FLAGS_MAGNIFICATION_CALLBACK, "windowState=" + windowState + ";shown=" + shown);
}
final int displayId = windowState.getDisplayId();
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
@@ -555,7 +583,7 @@
+ "mWindowsForAccessibilityObserver=" + mWindowsForAccessibilityObserver);
}
- private void removeObserverOfEmbeddedDisplay(WindowsForAccessibilityObserver
+ private void removeObserversForEmbeddedChildDisplays(WindowsForAccessibilityObserver
observerOfParentDisplay) {
final IntArray embeddedDisplayIdList =
observerOfParentDisplay.getAndClearEmbeddedDisplayIdList();
@@ -599,7 +627,7 @@
private final Handler mHandler;
private final DisplayContent mDisplayContent;
private final Display mDisplay;
- private final AccessibilityTracing mAccessibilityTracing;
+ private final AccessibilityControllerInternalImpl mAccessibilityTracing;
private final MagnificationCallbacks mCallbacks;
@@ -618,11 +646,13 @@
mDisplay = display;
mHandler = new MyHandler(mService.mH.getLooper());
mMagnifedViewport = new MagnifiedViewport();
- mAccessibilityTracing = AccessibilityTracing.getInstance(mService);
+ mAccessibilityTracing =
+ AccessibilityController.getAccessibilityControllerInternal(mService);
mLongAnimationDuration = mDisplayContext.getResources().getInteger(
com.android.internal.R.integer.config_longAnimTime);
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".DisplayMagnifier.constructor",
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".DisplayMagnifier.constructor",
+ FLAGS_MAGNIFICATION_CALLBACK,
"windowManagerService={" + windowManagerService + "}; displayContent={"
+ displayContent + "}; display={" + display + "}; callbacks={"
+ callbacks + "}");
@@ -630,9 +660,9 @@
}
void setMagnificationSpec(MagnificationSpec spec) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
- LOG_TAG + ".setMagnificationSpec", "spec={" + spec + "}");
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".setMagnificationSpec",
+ FLAGS_MAGNIFICATION_CALLBACK, "spec={" + spec + "}");
}
mMagnifedViewport.updateMagnificationSpec(spec);
mMagnifedViewport.recomputeBounds();
@@ -642,25 +672,26 @@
}
void setForceShowMagnifiableBounds(boolean show) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
- LOG_TAG + ".setForceShowMagnifiableBounds", "show=" + show);
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".setForceShowMagnifiableBounds",
+ FLAGS_MAGNIFICATION_CALLBACK, "show=" + show);
}
mForceShowMagnifiableBounds = show;
mMagnifedViewport.setMagnifiedRegionBorderShown(show, true);
}
boolean isForceShowingMagnifiableBounds() {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".isForceShowingMagnifiableBounds");
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".isForceShowingMagnifiableBounds",
+ FLAGS_MAGNIFICATION_CALLBACK);
}
return mForceShowMagnifiableBounds;
}
void onRectangleOnScreenRequested(Rect rectangle) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
- LOG_TAG + ".onRectangleOnScreenRequested", "rectangle={" + rectangle + "}");
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".onRectangleOnScreenRequested",
+ FLAGS_MAGNIFICATION_CALLBACK, "rectangle={" + rectangle + "}");
}
if (DEBUG_RECTANGLE_REQUESTED) {
Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle);
@@ -683,8 +714,9 @@
}
void onWindowLayersChanged() {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".onWindowLayersChanged");
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(
+ LOG_TAG + ".onWindowLayersChanged", FLAGS_MAGNIFICATION_CALLBACK);
}
if (DEBUG_LAYERS) {
Slog.i(LOG_TAG, "Layers changed.");
@@ -694,9 +726,9 @@
}
void onRotationChanged(DisplayContent displayContent) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
- LOG_TAG + ".onRotationChanged", "displayContent={" + displayContent + "}");
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".onRotationChanged",
+ FLAGS_MAGNIFICATION_CALLBACK, "displayContent={" + displayContent + "}");
}
if (DEBUG_ROTATION) {
final int rotation = displayContent.getRotation();
@@ -708,8 +740,9 @@
}
void onAppWindowTransition(int displayId, int transition) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".onAppWindowTransition",
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".onAppWindowTransition",
+ FLAGS_MAGNIFICATION_CALLBACK,
"displayId=" + displayId + "; transition=" + transition);
}
if (DEBUG_WINDOW_TRANSITIONS) {
@@ -733,8 +766,9 @@
}
void onWindowTransition(WindowState windowState, int transition) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".onWindowTransition",
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".onWindowTransition",
+ FLAGS_MAGNIFICATION_CALLBACK,
"windowState={" + windowState + "}; transition=" + transition);
}
if (DEBUG_WINDOW_TRANSITIONS) {
@@ -791,18 +825,18 @@
}
void onImeSurfaceShownChanged(boolean shown) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
- LOG_TAG + ".onImeSurfaceShownChanged", "shown=" + shown);
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".onImeSurfaceShownChanged",
+ FLAGS_MAGNIFICATION_CALLBACK, "shown=" + shown);
}
mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_IME_WINDOW_VISIBILITY_CHANGED,
shown ? 1 : 0, 0).sendToTarget();
}
MagnificationSpec getMagnificationSpecForWindow(WindowState windowState) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".getMagnificationSpecForWindow",
- "windowState={" + windowState + "}");
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".getMagnificationSpecForWindow",
+ FLAGS_MAGNIFICATION_CALLBACK, "windowState={" + windowState + "}");
}
MagnificationSpec spec = mMagnifedViewport.getMagnificationSpec();
if (spec != null && !spec.isNop()) {
@@ -814,8 +848,9 @@
}
void getMagnificationRegion(Region outMagnificationRegion) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".getMagnificationRegion",
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".getMagnificationRegion",
+ FLAGS_MAGNIFICATION_CALLBACK,
"outMagnificationRegion={" + outMagnificationRegion + "}");
}
// Make sure we're working with the most current bounds
@@ -824,25 +859,26 @@
}
void destroy() {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".destroy");
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".destroy", FLAGS_MAGNIFICATION_CALLBACK);
}
mMagnifedViewport.destroyWindow();
}
// Can be called outside of a surface transaction
void showMagnificationBoundsIfNeeded() {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".showMagnificationBoundsIfNeeded");
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".showMagnificationBoundsIfNeeded",
+ FLAGS_MAGNIFICATION_CALLBACK);
}
mHandler.obtainMessage(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)
.sendToTarget();
}
void drawMagnifiedRegionBorderIfNeeded(SurfaceControl.Transaction t) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".drawMagnifiedRegionBorderIfNeeded",
- "transition={" + t + "}");
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".drawMagnifiedRegionBorderIfNeeded",
+ FLAGS_MAGNIFICATION_CALLBACK, "transition={" + t + "}");
}
mMagnifedViewport.drawWindowIfNeeded(t);
}
@@ -1481,7 +1517,7 @@
private final Handler mHandler;
- private final AccessibilityTracing mAccessibilityTracing;
+ private final AccessibilityControllerInternalImpl mAccessibilityTracing;
private final WindowsForAccessibilityCallback mCallback;
@@ -1501,24 +1537,26 @@
mCallback = callback;
mDisplayId = displayId;
mHandler = new MyHandler(mService.mH.getLooper());
- mAccessibilityTracing = AccessibilityTracing.getInstance(mService);
+ mAccessibilityTracing =
+ AccessibilityController.getAccessibilityControllerInternal(mService);
mRecurringAccessibilityEventsIntervalMillis = ViewConfiguration
.getSendRecurringAccessibilityEventsInterval();
computeChangedWindows(true);
}
void performComputeChangedWindows(boolean forceSend) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".performComputeChangedWindows",
- "forceSend=" + forceSend);
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".performComputeChangedWindows",
+ FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK, "forceSend=" + forceSend);
}
mHandler.removeMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS);
computeChangedWindows(forceSend);
}
void scheduleComputeChangedWindows() {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".scheduleComputeChangedWindows");
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".scheduleComputeChangedWindows",
+ FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK);
}
if (!mHandler.hasMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS)) {
mHandler.sendEmptyMessageDelayed(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS,
@@ -1541,15 +1579,22 @@
mEmbeddedDisplayIdList.add(displayId);
}
+ void notifyDisplayReparented(int embeddedDisplayId) {
+ // Notifies the A11y framework the display is reparented and
+ // becomes an embedded display for removing the un-used
+ // displayWindowObserver of this embedded one.
+ mCallback.onDisplayReparented(embeddedDisplayId);
+ }
+
/**
* Check if windows have changed, and send them to the accessibility subsystem if they have.
*
* @param forceSend Send the windows the accessibility even if they haven't changed.
*/
void computeChangedWindows(boolean forceSend) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
- LOG_TAG + ".computeChangedWindows", "forceSend=" + forceSend);
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".computeChangedWindows",
+ FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK, "forceSend=" + forceSend);
}
if (DEBUG) {
Slog.i(LOG_TAG, "computeChangedWindows()");
@@ -1895,8 +1940,8 @@
private static final class AccessibilityControllerInternalImpl
implements AccessibilityControllerInternal {
- private static AccessibilityControllerInternal sInstance;
- static AccessibilityControllerInternal getInstance(WindowManagerService service) {
+ private static AccessibilityControllerInternalImpl sInstance;
+ static AccessibilityControllerInternalImpl getInstance(WindowManagerService service) {
synchronized (STATIC_LOCK) {
if (sInstance == null) {
sInstance = new AccessibilityControllerInternalImpl(service);
@@ -1906,18 +1951,23 @@
}
private final AccessibilityTracing mTracing;
+ private volatile long mEnabledTracingFlags;
+
private AccessibilityControllerInternalImpl(WindowManagerService service) {
mTracing = AccessibilityTracing.getInstance(service);
+ mEnabledTracingFlags = 0L;
}
@Override
- public void startTrace() {
+ public void startTrace(long loggingTypes) {
+ mEnabledTracingFlags = loggingTypes;
mTracing.startTrace();
}
@Override
public void stopTrace() {
mTracing.stopTrace();
+ mEnabledTracingFlags = 0L;
}
@Override
@@ -1925,19 +1975,37 @@
return mTracing.isEnabled();
}
- @Override
- public void logTrace(
- String where, String callingParams, byte[] a11yDump, int callingUid,
- StackTraceElement[] stackTrace) {
- mTracing.logState(where, callingParams, a11yDump, callingUid, stackTrace);
+ boolean isTracingEnabled(long flags) {
+ return (flags & mEnabledTracingFlags) != 0L;
+ }
+
+ void logTrace(String where, long loggingTypes) {
+ logTrace(where, loggingTypes, "");
+ }
+
+ void logTrace(String where, long loggingTypes, String callingParams) {
+ logTrace(where, loggingTypes, callingParams, "".getBytes(), Binder.getCallingUid());
+ }
+
+ void logTrace(String where, long loggingTypes, String callingParams, byte[] a11yDump,
+ int callingUid) {
+ mTracing.logState(where, loggingTypes, callingParams, a11yDump, callingUid,
+ new HashSet<String>(Arrays.asList("logTrace")));
}
@Override
- public void logTrace(
- String where, String callingParams, byte[] a11yDump, int callingUid,
- StackTraceElement[] callStack, long timeStamp, int processId, long threadId) {
- mTracing.logState(where, callingParams, a11yDump, callingUid, callStack, timeStamp,
- processId, threadId);
+ public void logTrace(String where, long loggingTypes, String callingParams, byte[] a11yDump,
+ int callingUid, StackTraceElement[] stackTrace, Set<String> ignoreStackEntries) {
+ mTracing.logState(where, loggingTypes, callingParams, a11yDump, callingUid, stackTrace,
+ ignoreStackEntries);
+ }
+
+ @Override
+ public void logTrace(String where, long loggingTypes, String callingParams, byte[] a11yDump,
+ int callingUid, StackTraceElement[] callStack, long timeStamp, int processId,
+ long threadId, Set<String> ignoreStackEntries) {
+ mTracing.logState(where, loggingTypes, callingParams, a11yDump, callingUid, callStack,
+ timeStamp, processId, threadId, ignoreStackEntries);
}
}
@@ -1954,7 +2022,6 @@
private static final int BUFFER_CAPACITY = 1024 * 1024 * 12;
private static final String TRACE_FILENAME = "/data/misc/a11ytrace/a11y_trace.pb";
- private static final String TRACE_DIRECTORY = "/data/misc/a11ytrace/";
private static final String TAG = "AccessibilityTracing";
private static final long MAGIC_NUMBER_VALUE =
((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
@@ -1984,13 +2051,6 @@
return;
}
synchronized (mLock) {
- try {
- Files.createDirectories(Paths.get(TRACE_DIRECTORY));
- mTraceFile.createNewFile();
- } catch (Exception e) {
- Slog.e(TAG, "Error: Failed to create trace file.");
- return;
- }
mEnabled = true;
mBuffer.resetBuffer();
}
@@ -2021,86 +2081,127 @@
/**
* Write an accessibility trace log entry.
*/
- void logState(String where) {
+ void logState(String where, long loggingTypes) {
if (!mEnabled) {
return;
}
- logState(where, "");
+ logState(where, loggingTypes, "");
}
/**
* Write an accessibility trace log entry.
*/
- void logState(String where, String callingParams) {
+ void logState(String where, long loggingTypes, String callingParams) {
if (!mEnabled) {
return;
}
- logState(where, callingParams, "".getBytes());
+ logState(where, loggingTypes, callingParams, "".getBytes());
}
/**
* Write an accessibility trace log entry.
*/
- void logState(String where, String callingParams, byte[] a11yDump) {
+ void logState(String where, long loggingTypes, String callingParams, byte[] a11yDump) {
if (!mEnabled) {
return;
}
- logState(where, callingParams, a11yDump, Binder.getCallingUid());
+ logState(where, loggingTypes, callingParams, a11yDump, Binder.getCallingUid(),
+ new HashSet<String>(Arrays.asList("logState")));
}
/**
* Write an accessibility trace log entry.
*/
- void logState(
- String where, String callingParams, byte[] a11yDump, int callingUid) {
+ void logState(String where, long loggingTypes, String callingParams, byte[] a11yDump,
+ int callingUid, Set<String> ignoreStackEntries) {
if (!mEnabled) {
return;
}
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
-
- logState(where, callingParams, a11yDump, callingUid, stackTraceElements);
+ ignoreStackEntries.add("logState");
+ logState(where, loggingTypes, callingParams, a11yDump, callingUid, stackTraceElements,
+ ignoreStackEntries);
}
/**
* Write an accessibility trace log entry.
*/
- void logState(String where, String callingParams, byte[] a11yDump, int callingUid,
- StackTraceElement[] stackTrace) {
+ void logState(String where, long loggingTypes, String callingParams, byte[] a11yDump,
+ int callingUid, StackTraceElement[] stackTrace, Set<String> ignoreStackEntries) {
if (!mEnabled) {
return;
}
-
- log(where, callingParams, a11yDump, callingUid, stackTrace,
+ log(where, loggingTypes, callingParams, a11yDump, callingUid, stackTrace,
SystemClock.elapsedRealtimeNanos(),
Process.myPid() + ":" + Application.getProcessName(),
- Thread.currentThread().getId() + ":" + Thread.currentThread().getName());
+ Thread.currentThread().getId() + ":" + Thread.currentThread().getName(),
+ ignoreStackEntries);
}
/**
* Write an accessibility trace log entry.
*/
- void logState(String where, String callingParams, byte[] a11yDump, int callingUid,
- StackTraceElement[] callingStack, long timeStamp, int processId, long threadId) {
+ void logState(String where, long loggingTypes, String callingParams, byte[] a11yDump,
+ int callingUid, StackTraceElement[] callingStack, long timeStamp, int processId,
+ long threadId, Set<String> ignoreStackEntries) {
if (!mEnabled) {
return;
}
- log(where, callingParams, a11yDump, callingUid, callingStack, timeStamp,
- String.valueOf(processId), String.valueOf(threadId));
+ log(where, loggingTypes, callingParams, a11yDump, callingUid, callingStack, timeStamp,
+ String.valueOf(processId), String.valueOf(threadId), ignoreStackEntries);
}
- private String toStackTraceString(StackTraceElement[] stackTraceElements) {
+ private String toStackTraceString(StackTraceElement[] stackTraceElements,
+ Set<String> ignoreStackEntries) {
+
if (stackTraceElements == null) {
return "";
}
+
StringBuilder stringBuilder = new StringBuilder();
- boolean skip = true;
- for (int i = 0; i < stackTraceElements.length; i++) {
- if (stackTraceElements[i].toString().contains(
- AccessibilityTracing.class.getSimpleName())) {
- skip = false;
- } else if (!skip) {
- stringBuilder.append(stackTraceElements[i].toString()).append("\n");
+ int i = 0;
+
+ // Skip the first a few elements until after any ignoreStackEntries
+ int firstMatch = -1;
+ while (i < stackTraceElements.length) {
+ for (String ele : ignoreStackEntries) {
+ if (stackTraceElements[i].toString().contains(ele)) {
+ // found the first stack element containing the ignorable stack entries
+ firstMatch = i;
+ break;
+ }
}
+ if (firstMatch < 0) {
+ // Haven't found the first match yet, continue
+ i++;
+ } else {
+ break;
+ }
+ }
+ int lastMatch = firstMatch;
+ if (i < stackTraceElements.length) {
+ i++;
+ // Found the first match. Now look for the last match.
+ while (i < stackTraceElements.length) {
+ for (String ele : ignoreStackEntries) {
+ if (stackTraceElements[i].toString().contains(ele)) {
+ // This is a match. Look at the next stack element.
+ lastMatch = i;
+ break;
+ }
+ }
+ if (lastMatch != i) {
+ // Found a no-match.
+ break;
+ }
+ i++;
+ }
+ }
+
+ i = lastMatch + 1;
+ while (i < stackTraceElements.length) {
+ stringBuilder.append(stackTraceElements[i].toString()).append("\n");
+ i++;
}
return stringBuilder.toString();
}
@@ -2108,19 +2209,22 @@
/**
* Write the current state to the buffer
*/
- private void log(String where, String callingParams, byte[] a11yDump, int callingUid,
- StackTraceElement[] callingStack, long timeStamp, String processName,
- String threadName) {
+ private void log(String where, long loggingTypes, String callingParams, byte[] a11yDump,
+ int callingUid, StackTraceElement[] callingStack, long timeStamp,
+ String processName, String threadName, Set<String> ignoreStackEntries) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = timeStamp;
- args.arg2 = where;
- args.arg3 = processName;
- args.arg4 = threadName;
- args.arg5 = callingUid;
- args.arg6 = callingParams;
- args.arg7 = callingStack;
- args.arg8 = a11yDump;
- mHandler.obtainMessage(LogHandler.MESSAGE_LOG_TRACE_ENTRY, args).sendToTarget();
+ args.arg2 = loggingTypes;
+ args.arg3 = where;
+ args.arg4 = processName;
+ args.arg5 = threadName;
+ args.arg6 = ignoreStackEntries;
+ args.arg7 = callingParams;
+ args.arg8 = callingStack;
+ args.arg9 = a11yDump;
+
+ mHandler.obtainMessage(
+ LogHandler.MESSAGE_LOG_TRACE_ENTRY, callingUid, 0, args).sendToTarget();
}
/**
@@ -2149,8 +2253,6 @@
LocalServices.getService(PackageManagerInternal.class);
long tokenOuter = os.start(ENTRY);
- String callingStack =
- toStackTraceString((StackTraceElement[]) args.arg7);
long reportedTimeStampNanos = (long) args.arg1;
long currentElapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos();
@@ -2163,13 +2265,25 @@
os.write(ELAPSED_REALTIME_NANOS, reportedTimeStampNanos);
os.write(CALENDAR_TIME, fm.format(reportedTimeMillis).toString());
- os.write(WHERE, (String) args.arg2);
- os.write(PROCESS_NAME, (String) args.arg3);
- os.write(THREAD_ID_NAME, (String) args.arg4);
- os.write(CALLING_PKG, pmInternal.getNameForUid((int) args.arg5));
- os.write(CALLING_PARAMS, (String) args.arg6);
+
+ long loggingTypes = (long) args.arg2;
+ List<String> loggingTypeNames =
+ AccessibilityTrace.getNamesOfLoggingTypes(loggingTypes);
+
+ for (String type : loggingTypeNames) {
+ os.write(LOGGING_TYPE, type);
+ }
+ os.write(WHERE, (String) args.arg3);
+ os.write(PROCESS_NAME, (String) args.arg4);
+ os.write(THREAD_ID_NAME, (String) args.arg5);
+ os.write(CALLING_PKG, pmInternal.getNameForUid(message.arg1));
+ os.write(CALLING_PARAMS, (String) args.arg7);
+
+ String callingStack = toStackTraceString(
+ (StackTraceElement[]) args.arg8, (Set<String>) args.arg6);
+
os.write(CALLING_STACKS, callingStack);
- os.write(ACCESSIBILITY_SERVICE, (byte[]) args.arg8);
+ os.write(ACCESSIBILITY_SERVICE, (byte[]) args.arg9);
long tokenInner = os.start(WINDOW_MANAGER_SERVICE);
synchronized (mService.mGlobalLock) {
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 26f475e..a975ba6 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -28,6 +28,11 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IMMERSIVE;
+import static com.android.server.wm.ActivityRecord.State.DESTROYED;
+import static com.android.server.wm.ActivityRecord.State.DESTROYING;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESTARTING_PROCESS;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
@@ -35,8 +40,6 @@
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
import static com.android.server.wm.ActivityTaskManagerService.TAG_SWITCH;
import static com.android.server.wm.ActivityTaskManagerService.enforceNotIsolatedCaller;
-import static com.android.server.wm.Task.ActivityState.DESTROYED;
-import static com.android.server.wm.Task.ActivityState.DESTROYING;
import android.annotation.NonNull;
import android.app.Activity;
@@ -68,6 +71,7 @@
import android.util.Slog;
import android.view.RemoteAnimationDefinition;
import android.window.SizeConfigurationBuckets;
+import android.window.TransitionInfo;
import com.android.internal.app.AssistUtils;
import com.android.internal.policy.IKeyguardDismissCallback;
@@ -188,7 +192,7 @@
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityStopped");
r = ActivityRecord.isInRootTaskLocked(token);
if (r != null) {
- if (r.attachedToProcess() && r.isState(Task.ActivityState.RESTARTING_PROCESS)) {
+ if (r.attachedToProcess() && r.isState(RESTARTING_PROCESS)) {
// The activity was requested to restart from
// {@link #restartActivityProcessIfVisible}.
restartingName = r.app.mName;
@@ -1010,10 +1014,13 @@
final long origId = Binder.clearCallingIdentity();
synchronized (mGlobalLock) {
final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
- if (r != null && r.isState(Task.ActivityState.RESUMED, Task.ActivityState.PAUSING)) {
+ if (r != null && r.isState(RESUMED, PAUSING)) {
r.mDisplayContent.mAppTransition.overridePendingAppTransition(
packageName, enterAnim, exitAnim, null, null,
r.mOverrideTaskTransition);
+ mService.getTransitionController().setOverrideAnimation(
+ TransitionInfo.AnimationOptions.makeCustomAnimOptions(packageName,
+ enterAnim, exitAnim, r.mOverrideTaskTransition));
}
}
Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 2b6a838..d786b17 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -745,7 +745,7 @@
Slog.i(TAG, "notifyVisibilityChanged " + r + " visible=" + r.mVisibleRequested
+ " state=" + r.getState() + " finishing=" + r.finishing);
}
- if (r.isState(Task.ActivityState.RESUMED) && r.mDisplayContent.isSleeping()) {
+ if (r.isState(ActivityRecord.State.RESUMED) && r.mDisplayContent.isSleeping()) {
// The activity may be launching while keyguard is locked. The keyguard may be dismissed
// after the activity finished relayout, so skip the visibility check to avoid aborting
// the tracking of launch event.
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 45da45a..cb3afe4 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -128,6 +128,17 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SWITCH;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.ActivityRecord.State.DESTROYED;
+import static com.android.server.wm.ActivityRecord.State.DESTROYING;
+import static com.android.server.wm.ActivityRecord.State.FINISHING;
+import static com.android.server.wm.ActivityRecord.State.INITIALIZING;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESTARTING_PROCESS;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STARTED;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
import static com.android.server.wm.ActivityRecordProto.ALL_DRAWN;
import static com.android.server.wm.ActivityRecordProto.APP_STOPPED;
import static com.android.server.wm.ActivityRecordProto.CLIENT_VISIBLE;
@@ -190,18 +201,7 @@
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
-import static com.android.server.wm.Task.ActivityState.DESTROYED;
-import static com.android.server.wm.Task.ActivityState.DESTROYING;
-import static com.android.server.wm.Task.ActivityState.FINISHING;
-import static com.android.server.wm.Task.ActivityState.INITIALIZING;
-import static com.android.server.wm.Task.ActivityState.PAUSED;
-import static com.android.server.wm.Task.ActivityState.PAUSING;
-import static com.android.server.wm.Task.ActivityState.RESTARTING_PROCESS;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
-import static com.android.server.wm.Task.ActivityState.STARTED;
-import static com.android.server.wm.Task.ActivityState.STOPPED;
-import static com.android.server.wm.Task.ActivityState.STOPPING;
-import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE;
import static com.android.server.wm.TaskPersister.DEBUG;
import static com.android.server.wm.TaskPersister.IMAGE_EXTENSION;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
@@ -299,6 +299,7 @@
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
import android.view.RemoteAnimationTarget;
+import android.view.Surface.Rotation;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.WindowInsets.Type;
@@ -311,12 +312,14 @@
import android.window.SplashScreen;
import android.window.SplashScreenView.SplashScreenViewParcelable;
import android.window.TaskSnapshot;
+import android.window.TransitionInfo.AnimationOptions;
import android.window.WindowContainerToken;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ResolverActivity;
import com.android.internal.content.ReferrerIntent;
+import com.android.internal.os.TransferPipe;
import com.android.internal.policy.AttributeCache;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ToBooleanFunction;
@@ -332,7 +335,6 @@
import com.android.server.uri.UriPermissionOwner;
import com.android.server.wm.ActivityMetricsLogger.TransitionInfoSnapshot;
import com.android.server.wm.SurfaceAnimator.AnimationType;
-import com.android.server.wm.Task.ActivityState;
import com.android.server.wm.WindowManagerService.H;
import com.android.server.wm.utils.InsetUtils;
@@ -341,6 +343,7 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
@@ -484,7 +487,7 @@
ActivityServiceConnectionsHolder mServiceConnectionsHolder; // Service connections.
UriPermissionOwner uriPermissions; // current special URI access perms.
WindowProcessController app; // if non-null, hosting application
- private ActivityState mState; // current state we are in
+ private State mState; // current state we are in
private Bundle mIcicle; // last saved activity state
private PersistableBundle mPersistentState; // last persistently saved activity state
private boolean mHaveState = true; // Indicates whether the last saved state of activity is
@@ -546,6 +549,21 @@
static final int LAUNCH_SOURCE_TYPE_HOME = 2;
static final int LAUNCH_SOURCE_TYPE_SYSTEMUI = 3;
static final int LAUNCH_SOURCE_TYPE_APPLICATION = 4;
+
+ enum State {
+ INITIALIZING,
+ STARTED,
+ RESUMED,
+ PAUSING,
+ PAUSED,
+ STOPPING,
+ STOPPED,
+ FINISHING,
+ DESTROYING,
+ DESTROYED,
+ RESTARTING_PROCESS
+ }
+
/**
* The type of launch source.
*/
@@ -780,6 +798,7 @@
*/
private final Configuration mTmpConfig = new Configuration();
private final Rect mTmpBounds = new Rect();
+ private final Rect mTmpOutNonDecorBounds = new Rect();
// Token for targeting this activity for assist purposes.
final Binder assistToken = new Binder();
@@ -1125,6 +1144,76 @@
mLetterboxUiController.dump(pw, prefix);
}
+ static boolean dumpActivity(FileDescriptor fd, PrintWriter pw, int index, ActivityRecord r,
+ String prefix, String label, boolean complete, boolean brief, boolean client,
+ String dumpPackage, boolean needNL, Runnable header, Task lastTask) {
+ if (dumpPackage != null && !dumpPackage.equals(r.packageName)) {
+ return false;
+ }
+
+ final boolean full = !brief && (complete || !r.isInHistory());
+ if (needNL) {
+ pw.println("");
+ }
+ if (header != null) {
+ header.run();
+ }
+
+ String innerPrefix = prefix + " ";
+ String[] args = new String[0];
+ if (lastTask != r.getTask()) {
+ lastTask = r.getTask();
+ pw.print(prefix);
+ pw.print(full ? "* " : " ");
+ pw.println(lastTask);
+ if (full) {
+ lastTask.dump(pw, prefix + " ");
+ } else if (complete) {
+ // Complete + brief == give a summary. Isn't that obvious?!?
+ if (lastTask.intent != null) {
+ pw.print(prefix);
+ pw.print(" ");
+ pw.println(lastTask.intent.toInsecureString());
+ }
+ }
+ }
+ pw.print(prefix); pw.print(full ? " * " : " "); pw.print(label);
+ pw.print(" #"); pw.print(index); pw.print(": ");
+ pw.println(r);
+ if (full) {
+ r.dump(pw, innerPrefix, true /* dumpAll */);
+ } else if (complete) {
+ // Complete + brief == give a summary. Isn't that obvious?!?
+ pw.print(innerPrefix);
+ pw.println(r.intent.toInsecureString());
+ if (r.app != null) {
+ pw.print(innerPrefix);
+ pw.println(r.app);
+ }
+ }
+ if (client && r.attachedToProcess()) {
+ // flush anything that is already in the PrintWriter since the thread is going
+ // to write to the file descriptor directly
+ pw.flush();
+ try {
+ TransferPipe tp = new TransferPipe();
+ try {
+ r.app.getThread().dumpActivity(
+ tp.getWriteFd(), r.appToken, innerPrefix, args);
+ // Short timeout, since blocking here can deadlock with the application.
+ tp.go(fd, 2000);
+ } finally {
+ tp.kill();
+ }
+ } catch (IOException e) {
+ pw.println(innerPrefix + "Failure while dumping the activity: " + e);
+ } catch (RemoteException e) {
+ pw.println(innerPrefix + "Got a RemoteException while dumping the activity");
+ }
+ }
+ return true;
+ }
+
void setAppTimeTracker(AppTimeTracker att) {
appTimeTracker = att;
}
@@ -1237,7 +1326,7 @@
// If the activity is in stopping or stopped state, for instance, it's in the
// split screen task and not the top one, the last configuration it should keep
// is the one before multi-window mode change.
- final ActivityState state = getState();
+ final State state = getState();
if (state != STOPPED && state != STOPPING) {
ensureActivityConfiguration(0 /* globalChanges */, PRESERVE_WINDOWS,
true /* ignoreVisibility */);
@@ -1270,21 +1359,30 @@
private void computeConfigurationAfterMultiWindowModeChange() {
final Configuration newConfig = new Configuration();
- newConfig.setTo(task.getRequestedOverrideConfiguration());
+ final TaskFragment taskFrag = getTaskFragment();
+ newConfig.setTo(taskFrag.getRequestedOverrideConfiguration());
Rect outBounds = newConfig.windowConfiguration.getBounds();
- final Configuration parentConfig = task.getParent().getConfiguration();
- task.adjustForMinimalTaskDimensions(outBounds, outBounds, parentConfig);
- task.computeConfigResourceOverrides(newConfig, parentConfig);
+ // TODO(b/189384393): which parent does this Activity really needed?
+ // I guess we should either call getRootTask or getDisplayArea for this case.
+ final Configuration parentConfig = taskFrag.getParent().getConfiguration();
+ taskFrag.adjustForMinimalTaskDimensions(outBounds, outBounds, parentConfig);
+ taskFrag.computeConfigResourceOverrides(newConfig, parentConfig);
}
Task getTask() {
return task;
}
+ @Nullable
+ TaskFragment getTaskFragment() {
+ WindowContainer parent = getParent();
+ return parent != null ? parent.asTaskFragment() : null;
+ }
+
@Override
void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
- final Task oldTask = oldParent != null ? (Task) oldParent : null;
- final Task newTask = newParent != null ? (Task) newParent : null;
+ final Task oldTask = oldParent != null ? ((TaskFragment) oldParent).getTask() : null;
+ final Task newTask = newParent != null ? ((TaskFragment) newParent).getTask() : null;
this.task = newTask;
super.onParentChanged(newParent, oldParent);
@@ -1342,11 +1440,12 @@
updateColorTransform();
- if (oldTask != null) {
- oldTask.cleanUpActivityReferences(this);
+ if (oldParent != null) {
+ ((TaskFragment) oldParent).cleanUpActivityReferences(this);
}
- if (newTask != null && isState(RESUMED)) {
- newTask.setResumedActivity(this, "onParentChanged");
+
+ if (newParent != null && isState(RESUMED)) {
+ ((TaskFragment) newParent).setResumedActivity(this, "onParentChanged");
}
if (rootTask != null && rootTask.topRunningActivity() == this) {
@@ -2275,23 +2374,24 @@
}
/**
- * Reparents this activity into {@param newTask} at the provided {@param position}. The caller
- * should ensure that the {@param newTask} is not already the parent of this activity.
+ * Reparents this activity into {@param newTaskFrag} at the provided {@param position}. The
+ * caller should ensure that the {@param newTaskFrag} is not already the parent of this
+ * activity.
*/
- void reparent(Task newTask, int position, String reason) {
+ void reparent(TaskFragment newTaskFrag, int position, String reason) {
if (getParent() == null) {
Slog.w(TAG, "reparent: Attempted to reparent non-existing app token: " + appToken);
return;
}
- final Task prevTask = task;
- if (prevTask == newTask) {
- throw new IllegalArgumentException(reason + ": task=" + newTask
+ final TaskFragment prevTaskFrag = getTaskFragment();
+ if (prevTaskFrag == newTaskFrag) {
+ throw new IllegalArgumentException(reason + ": task fragment =" + newTaskFrag
+ " is already the parent of r=" + this);
}
ProtoLog.i(WM_DEBUG_ADD_REMOVE, "reparent: moving activity=%s"
- + " to task=%d at %d", this, task.mTaskId, position);
- reparent(newTask, position);
+ + " to new task fragment in task=%d at %d", this, task.mTaskId, position);
+ reparent(newTaskFrag, position);
}
private boolean isHomeIntent(Intent intent) {
@@ -2857,7 +2957,7 @@
}
final Task rootTask = getRootTask();
- final boolean mayAdjustTop = (isState(RESUMED) || rootTask.getResumedActivity() == null)
+ final boolean mayAdjustTop = (isState(RESUMED) || rootTask.getTopResumedActivity() == null)
&& rootTask.isFocusedRootTaskOnDisplay()
// Do not adjust focus task because the task will be reused to launch new activity.
&& !task.isClearingToReuseTask();
@@ -2936,12 +3036,12 @@
// Tell window manager to prepare for this one to be removed.
setVisibility(false);
- if (task.getPausingActivity() == null) {
+ if (task.getTopPausingActivity() == null) {
ProtoLog.v(WM_DEBUG_STATES, "Finish needs to pause: %s", this);
if (DEBUG_USER_LEAVING) {
Slog.v(TAG_USER_LEAVING, "finish() => pause with userLeaving=false");
}
- task.startPausingLocked(false /* userLeaving */, false /* uiSleeping */,
+ task.startPausing(false /* userLeaving */, false /* uiSleeping */,
null /* resuming */, "finish");
}
@@ -3260,8 +3360,8 @@
if (DEBUG_SWITCH) {
final Task task = getTask();
Slog.v(TAG_SWITCH, "Safely destroying " + this + " in state " + getState()
- + " resumed=" + task.getResumedActivity()
- + " pausing=" + task.getPausingActivity()
+ + " resumed=" + task.getTopResumedActivity()
+ + " pausing=" + task.getTopPausingActivity()
+ " for reason " + reason);
}
return destroyImmediately(reason);
@@ -3340,7 +3440,7 @@
* Note: Call before {@link #removeFromHistory(String)}.
*/
void cleanUp(boolean cleanServices, boolean setState) {
- task.cleanUpActivityReferences(this);
+ getTaskFragment().cleanUpActivityReferences(this);
clearLastParentBeforePip();
deferRelaunchUntilPaused = false;
@@ -3445,7 +3545,7 @@
// failed more than twice. Skip activities that's already finishing cleanly by itself.
remove = false;
} else if ((!mHaveState && !stateNotNeeded
- && !isState(ActivityState.RESTARTING_PROCESS)) || finishing) {
+ && !isState(State.RESTARTING_PROCESS)) || finishing) {
// Don't currently have state for the activity, or it is finishing -- always remove it.
remove = true;
} else if (!mVisibleRequested && launchCount > 2
@@ -4169,6 +4269,7 @@
private void applyOptionsAnimation(ActivityOptions pendingOptions, Intent intent) {
final int animationType = pendingOptions.getAnimationType();
final DisplayContent displayContent = getDisplayContent();
+ AnimationOptions options = null;
switch (animationType) {
case ANIM_CUSTOM:
displayContent.mAppTransition.overridePendingAppTransition(
@@ -4178,11 +4279,17 @@
pendingOptions.getAnimationStartedListener(),
pendingOptions.getAnimationFinishedListener(),
pendingOptions.getOverrideTaskTransition());
+ options = AnimationOptions.makeCustomAnimOptions(pendingOptions.getPackageName(),
+ pendingOptions.getCustomEnterResId(), pendingOptions.getCustomExitResId(),
+ pendingOptions.getOverrideTaskTransition());
break;
case ANIM_CLIP_REVEAL:
displayContent.mAppTransition.overridePendingAppTransitionClipReveal(
pendingOptions.getStartX(), pendingOptions.getStartY(),
pendingOptions.getWidth(), pendingOptions.getHeight());
+ options = AnimationOptions.makeClipRevealAnimOptions(
+ pendingOptions.getStartX(), pendingOptions.getStartY(),
+ pendingOptions.getWidth(), pendingOptions.getHeight());
if (intent.getSourceBounds() == null) {
intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
pendingOptions.getStartY(),
@@ -4194,6 +4301,9 @@
displayContent.mAppTransition.overridePendingAppTransitionScaleUp(
pendingOptions.getStartX(), pendingOptions.getStartY(),
pendingOptions.getWidth(), pendingOptions.getHeight());
+ options = AnimationOptions.makeScaleUpAnimOptions(
+ pendingOptions.getStartX(), pendingOptions.getStartY(),
+ pendingOptions.getWidth(), pendingOptions.getHeight());
if (intent.getSourceBounds() == null) {
intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
pendingOptions.getStartY(),
@@ -4209,6 +4319,8 @@
pendingOptions.getStartX(), pendingOptions.getStartY(),
pendingOptions.getAnimationStartedListener(),
scaleUp);
+ options = AnimationOptions.makeThumnbnailAnimOptions(buffer,
+ pendingOptions.getStartX(), pendingOptions.getStartY(), scaleUp);
if (intent.getSourceBounds() == null && buffer != null) {
intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
pendingOptions.getStartY(),
@@ -4248,6 +4360,7 @@
case ANIM_OPEN_CROSS_PROFILE_APPS:
displayContent.mAppTransition
.overridePendingAppTransitionStartCrossProfileApps();
+ options = AnimationOptions.makeCrossProfileAnimOptions();
break;
case ANIM_NONE:
case ANIM_UNDEFINED:
@@ -4256,6 +4369,10 @@
Slog.e(TAG_WM, "applyOptionsLocked: Unknown animationType=" + animationType);
break;
}
+
+ if (options != null) {
+ mAtmService.getTransitionController().setOverrideAnimation(options);
+ }
}
void clearAllDrawn() {
@@ -4552,7 +4669,7 @@
}
// If in a transition, defer commits for activities that are going invisible
- if (!visible && mAtmService.getTransitionController().inTransition()) {
+ if (!visible && mAtmService.getTransitionController().inTransition(this)) {
return;
}
// If we are preparing an app transition, then delay changing
@@ -4792,7 +4909,7 @@
return mCurrentLaunchCanTurnScreenOn;
}
- void setState(ActivityState state, String reason) {
+ void setState(State state, String reason) {
ProtoLog.v(WM_DEBUG_STATES, "State movement: %s from:%s to:%s reason:%s",
this, getState(), state, reason);
@@ -4804,8 +4921,8 @@
mState = state;
- if (task != null) {
- task.onActivityStateChanged(this, state, reason);
+ if (getTaskFragment() != null) {
+ getTaskFragment().onActivityStateChanged(this, state, reason);
}
// The WindowManager interprets the app stopping signal as
@@ -4859,44 +4976,42 @@
}
}
- ActivityState getState() {
+ State getState() {
return mState;
}
/**
* Returns {@code true} if the Activity is in the specified state.
*/
- boolean isState(ActivityState state) {
+ boolean isState(State state) {
return state == mState;
}
/**
* Returns {@code true} if the Activity is in one of the specified states.
*/
- boolean isState(ActivityState state1, ActivityState state2) {
+ boolean isState(State state1, State state2) {
return state1 == mState || state2 == mState;
}
/**
* Returns {@code true} if the Activity is in one of the specified states.
*/
- boolean isState(ActivityState state1, ActivityState state2, ActivityState state3) {
+ boolean isState(State state1, State state2, State state3) {
return state1 == mState || state2 == mState || state3 == mState;
}
/**
* Returns {@code true} if the Activity is in one of the specified states.
*/
- boolean isState(ActivityState state1, ActivityState state2, ActivityState state3,
- ActivityState state4) {
+ boolean isState(State state1, State state2, State state3, State state4) {
return state1 == mState || state2 == mState || state3 == mState || state4 == mState;
}
/**
* Returns {@code true} if the Activity is in one of the specified states.
*/
- boolean isState(ActivityState state1, ActivityState state2, ActivityState state3,
- ActivityState state4, ActivityState state5) {
+ boolean isState(State state1, State state2, State state3, State state4, State state5) {
return state1 == mState || state2 == mState || state3 == mState || state4 == mState
|| state5 == mState;
}
@@ -4904,8 +5019,8 @@
/**
* Returns {@code true} if the Activity is in one of the specified states.
*/
- boolean isState(ActivityState state1, ActivityState state2, ActivityState state3,
- ActivityState state4, ActivityState state5, ActivityState state6) {
+ boolean isState(State state1, State state2, State state3, State state4, State state5,
+ State state6) {
return state1 == mState || state2 == mState || state3 == mState || state4 == mState
|| state5 == mState || state6 == mState;
}
@@ -5111,7 +5226,7 @@
// If the app is capable of entering PIP, we should try pausing it now
// so it can PIP correctly.
if (deferHidingClient) {
- task.startPausingLocked(false /* uiSleeping */,
+ getTaskFragment().startPausing(false /* uiSleeping */,
null /* resuming */, "makeInvisible");
break;
}
@@ -5211,7 +5326,8 @@
*/
private boolean shouldBeResumed(ActivityRecord activeActivity) {
return shouldMakeActive(activeActivity) && isFocusable()
- && getTask().getVisibility(activeActivity) == TASK_VISIBILITY_VISIBLE
+ && getTaskFragment().getVisibility(activeActivity)
+ == TASK_FRAGMENT_VISIBILITY_VISIBLE
&& canResumeByCompat();
}
@@ -5354,16 +5470,17 @@
ProtoLog.v(WM_DEBUG_STATES, "Activity paused: token=%s, timeout=%b", appToken,
timeout);
- if (task != null) {
+ final TaskFragment taskFragment = getTaskFragment();
+ if (taskFragment != null) {
removePauseTimeout();
- final ActivityRecord pausingActivity = task.getPausingActivity();
+ final ActivityRecord pausingActivity = taskFragment.getPausingActivity();
if (pausingActivity == this) {
ProtoLog.v(WM_DEBUG_STATES, "Moving to PAUSED: %s %s", this,
(timeout ? "(due to timeout)" : " (pause complete)"));
mAtmService.deferWindowLayout();
try {
- task.completePauseLocked(true /* resumeNext */, null /* resumingActivity */);
+ taskFragment.completePause(true /* resumeNext */, null /* resumingActivity */);
} finally {
mAtmService.continueWindowLayout();
}
@@ -6012,9 +6129,9 @@
return this;
}
// Try to use the one which is closest to top.
- ActivityRecord r = rootTask.getResumedActivity();
+ ActivityRecord r = rootTask.getTopResumedActivity();
if (r == null) {
- r = rootTask.getPausingActivity();
+ r = rootTask.getTopPausingActivity();
}
if (r != null) {
return r;
@@ -6092,7 +6209,8 @@
// This would be redundant.
return false;
}
- if (isState(RESUMED) || getRootTask() == null || this == task.getPausingActivity()
+ if (isState(RESUMED) || getRootTask() == null
+ || this == getTaskFragment().getPausingActivity()
|| !mHaveState || !stopped) {
// We're not ready for this kind of thing.
return false;
@@ -6608,8 +6726,7 @@
}
final Configuration displayConfig = mDisplayContent.getConfiguration();
return getDisplayContent().mAppTransition.createThumbnailAspectScaleAnimationLocked(
- appRect, insets, thumbnailHeader, task, displayConfig.uiMode,
- displayConfig.orientation);
+ appRect, insets, thumbnailHeader, task, displayConfig.orientation);
}
@Override
@@ -7034,7 +7151,8 @@
// If the activity has requested override bounds, the configuration needs to be
// computed accordingly.
if (!matchParentBounds()) {
- task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration);
+ getTaskFragment().computeConfigResourceOverrides(resolvedConfig,
+ newParentConfiguration);
}
// If activity in fullscreen mode is letterboxed because of fixed orientation then bounds
// are already calculated in resolveFixedOrientationConfiguration.
@@ -7149,7 +7267,7 @@
}
// Since bounds has changed, the configuration needs to be computed accordingly.
- task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration);
+ getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration);
}
/**
@@ -7165,8 +7283,53 @@
}
/**
- * Computes bounds (letterbox or pillarbox) when the parent doesn't handle the orientation
- * change and the requested orientation is different from the parent.
+ * In some cases, applying insets to bounds changes the orientation. For example, if a
+ * close-to-square display rotates to portrait to respect a portrait orientation activity, after
+ * insets such as the status and nav bars are applied, the activity may actually have a
+ * landscape orientation. This method checks whether the orientations of the activity window
+ * with and without insets match or if the orientation with insets already matches the
+ * requested orientation. If not, it may be necessary to letterbox the window.
+ * @param parentBounds are the new parent bounds passed down to the activity and should be used
+ * to compute the stable bounds.
+ * @param outBounds will store the stable bounds, which are the bounds with insets applied.
+ * These should be used to compute letterboxed bounds if orientation is not
+ * respected when insets are applied.
+ */
+ private boolean orientationRespectedWithInsets(Rect parentBounds, Rect outBounds) {
+ if (mDisplayContent == null) {
+ return true;
+ }
+ // Only need to make changes if activity sets an orientation
+ final int requestedOrientation = getRequestedConfigurationOrientation();
+ if (requestedOrientation == ORIENTATION_UNDEFINED) {
+ return true;
+ }
+ // Compute parent orientation from bounds
+ final int orientation = parentBounds.height() >= parentBounds.width()
+ ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
+ // Compute orientation from stable parent bounds (= parent bounds with insets applied)
+ final Task task = getTask();
+ task.calculateInsetFrames(mTmpOutNonDecorBounds /* outNonDecorBounds */,
+ outBounds /* outStableBounds */, parentBounds /* bounds */,
+ mDisplayContent.getDisplayInfo());
+ final int orientationWithInsets = outBounds.height() >= outBounds.width()
+ ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
+ // If orientation does not match the orientation with insets applied, then a
+ // display rotation will not be enough to respect orientation. However, even if they do
+ // not match but the orientation with insets applied matches the requested orientation, then
+ // there is no need to modify the bounds because when insets are applied, the activity will
+ // have the desired orientation.
+ return orientation == orientationWithInsets
+ || orientationWithInsets == requestedOrientation;
+ }
+
+ /**
+ * Computes bounds (letterbox or pillarbox) when either:
+ * 1. The parent doesn't handle the orientation change and the requested orientation is
+ * different from the parent (see {@link DisplayContent#setIgnoreOrientationRequest()}.
+ * 2. The parent handling the orientation is not enough. This occurs when the display rotation
+ * may not be enough to respect orientation requests (see {@link
+ * ActivityRecord#orientationRespectedWithInsets}).
*
* <p>If letterboxed due to fixed orientation then aspect ratio restrictions are also applied
* in this method.
@@ -7174,9 +7337,14 @@
private void resolveFixedOrientationConfiguration(@NonNull Configuration newParentConfig,
int windowingMode) {
mLetterboxBoundsForFixedOrientationAndAspectRatio = null;
- if (handlesOrientationChangeFromDescendant()) {
+ final Rect parentBounds = newParentConfig.windowConfiguration.getBounds();
+ final Rect containerBounds = new Rect(parentBounds);
+ boolean orientationRespectedWithInsets =
+ orientationRespectedWithInsets(parentBounds, containerBounds);
+ if (handlesOrientationChangeFromDescendant() && orientationRespectedWithInsets) {
// No need to letterbox because of fixed orientation. Display will handle
- // fixed-orientation requests.
+ // fixed-orientation requests and a display rotation is enough to respect requested
+ // orientation with insets applied.
return;
}
if (WindowConfiguration.inMultiWindowMode(windowingMode) && isResizeable()) {
@@ -7196,7 +7364,8 @@
// If the activity requires a different orientation (either by override or activityInfo),
// make it fit the available bounds by scaling down its bounds.
final int forcedOrientation = getRequestedConfigurationOrientation();
- if (forcedOrientation == ORIENTATION_UNDEFINED || forcedOrientation == parentOrientation) {
+ if (forcedOrientation == ORIENTATION_UNDEFINED
+ || (forcedOrientation == parentOrientation && orientationRespectedWithInsets)) {
return;
}
@@ -7208,60 +7377,83 @@
return;
}
- final Rect parentBounds = newParentConfig.windowConfiguration.getBounds();
- final int parentWidth = parentBounds.width();
- final int parentHeight = parentBounds.height();
- float aspect = Math.max(parentWidth, parentHeight)
- / (float) Math.min(parentWidth, parentHeight);
+ // TODO(b/182268157) merge aspect ratio logic here and in
+ // {@link ActivityRecord#applyAspectRatio}
+ // if no aspect ratio constraints are provided, parent aspect ratio is used
+ float aspectRatio = computeAspectRatio(parentBounds);
// Override from config_fixedOrientationLetterboxAspectRatio or via ADB with
// set-fixed-orientation-letterbox-aspect-ratio.
final float letterboxAspectRatioOverride =
mWmService.mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio();
- aspect = letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO
- ? letterboxAspectRatioOverride : aspect;
+ aspectRatio = letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO
+ ? letterboxAspectRatioOverride : aspectRatio;
// Adjust the fixed orientation letterbox bounds to fit the app request aspect ratio in
// order to use the extra available space.
final float maxAspectRatio = info.getMaxAspectRatio();
final float minAspectRatio = info.getMinAspectRatio();
- if (aspect > maxAspectRatio && maxAspectRatio != 0) {
- aspect = maxAspectRatio;
- } else if (aspect < minAspectRatio) {
- aspect = minAspectRatio;
+ if (aspectRatio > maxAspectRatio && maxAspectRatio != 0) {
+ aspectRatio = maxAspectRatio;
+ } else if (aspectRatio < minAspectRatio) {
+ aspectRatio = minAspectRatio;
}
// Store the current bounds to be able to revert to size compat mode values below if needed.
- Rect mTmpFullBounds = new Rect(resolvedBounds);
+ final Rect prevResolvedBounds = new Rect(resolvedBounds);
+
+ // Compute other dimension based on aspect ratio. Use bounds intersected with insets, stored
+ // in containerBounds after calling {@link ActivityRecord#orientationRespectedWithInsets()},
+ // to ensure that aspect ratio is respected after insets are applied.
+ int activityWidth;
+ int activityHeight;
if (forcedOrientation == ORIENTATION_LANDSCAPE) {
- final int height = (int) Math.rint(parentWidth / aspect);
- final int top = parentBounds.centerY() - height / 2;
- resolvedBounds.set(parentBounds.left, top, parentBounds.right, top + height);
+ activityWidth = parentBounds.width();
+ // Compute height from stable bounds width to ensure orientation respected after insets.
+ activityHeight = (int) Math.rint(containerBounds.width() / aspectRatio);
+ // Landscape is defined as width > height. To ensure activity is landscape when aspect
+ // ratio is close to 1, reduce the height by one pixel.
+ if (activityWidth == activityHeight) {
+ activityHeight -= 1;
+ }
+ // Center vertically within stable bounds in landscape to ensure insets do not trim
+ // height.
+ final int top = containerBounds.centerY() - activityHeight / 2;
+ resolvedBounds.set(parentBounds.left, top, parentBounds.right, top + activityHeight);
} else {
- final int width = (int) Math.rint(parentHeight / aspect);
+ activityHeight = parentBounds.height();
+ // Compute width from stable bounds height to ensure orientation respected after insets.
+ activityWidth = (int) Math.rint(containerBounds.height() / aspectRatio);
+ // Center horizontally in portrait. For now, align to left and allow
+ // {@link ActivityRecord#updateResolvedBoundsHorizontalPosition()} to center
+ // horizontally. Exclude left insets from parent to ensure cutout does not trim width.
final Rect parentAppBounds = newParentConfig.windowConfiguration.getAppBounds();
- final int left = width <= parentAppBounds.width()
- // Avoid overlapping with the horizontal decor area when possible.
- ? parentAppBounds.left : parentBounds.centerX() - width / 2;
- resolvedBounds.set(left, parentBounds.top, left + width, parentBounds.bottom);
+ resolvedBounds.set(parentAppBounds.left, parentBounds.top,
+ parentAppBounds.left + activityWidth, parentBounds.bottom);
}
if (mCompatDisplayInsets != null) {
mCompatDisplayInsets.getBoundsByRotation(
mTmpBounds, newParentConfig.windowConfiguration.getRotation());
- if (resolvedBounds.width() != mTmpBounds.width()
- || resolvedBounds.height() != mTmpBounds.height()) {
+ // Insets may differ between different rotations, for example in the case of a display
+ // cutout. To ensure consistent bounds across rotations, compare the activity dimensions
+ // minus insets from the rotation the compat bounds were computed in.
+ Task.intersectWithInsetsIfFits(mTmpBounds, parentBounds,
+ mCompatDisplayInsets.mStableInsets[mCompatDisplayInsets.mOriginalRotation]);
+ if (activityWidth != mTmpBounds.width()
+ || activityHeight != mTmpBounds.height()) {
// The app shouldn't be resized, we only do fixed orientation letterboxing if the
// compat bounds are also from the same fixed orientation letterbox. Otherwise,
// clear the fixed orientation bounds to show app in size compat mode.
- resolvedBounds.set(mTmpFullBounds);
+ resolvedBounds.set(prevResolvedBounds);
return;
}
}
// Calculate app bounds using fixed orientation bounds because they will be needed later
// for comparison with size compat app bounds in {@link resolveSizeCompatModeConfiguration}.
- task.computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig);
+ getTaskFragment().computeConfigResourceOverrides(getResolvedOverrideConfiguration(),
+ newParentConfig);
mLetterboxBoundsForFixedOrientationAndAspectRatio = new Rect(resolvedBounds);
}
@@ -7291,7 +7483,7 @@
if (!resolvedBounds.isEmpty() && !resolvedBounds.equals(parentBounds)) {
// Compute the configuration based on the resolved bounds. If aspect ratio doesn't
// restrict, the bounds should be the requested override bounds.
- task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
+ getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
getFixedRotationTransformDisplayInfo());
}
}
@@ -7358,10 +7550,10 @@
// Use resolvedBounds to compute other override configurations such as appBounds. The bounds
// are calculated in compat container space. The actual position on screen will be applied
// later, so the calculation is simpler that doesn't need to involve offset from parent.
- task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
+ getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
mCompatDisplayInsets);
// Use current screen layout as source because the size of app is independent to parent.
- resolvedConfig.screenLayout = Task.computeScreenLayoutOverride(
+ resolvedConfig.screenLayout = TaskFragment.computeScreenLayoutOverride(
getConfiguration().screenLayout, resolvedConfig.screenWidthDp,
resolvedConfig.screenHeightDp);
@@ -8509,6 +8701,8 @@
* compatibility mode activity compute the configuration without relying on its current display.
*/
static class CompatDisplayInsets {
+ /** The original rotation the compat insets were computed in */
+ final @Rotation int mOriginalRotation;
/** The container width on rotation 0. */
private final int mWidth;
/** The container height on rotation 0. */
@@ -8535,6 +8729,7 @@
/** Constructs the environment to simulate the bounds behavior of the given container. */
CompatDisplayInsets(DisplayContent display, ActivityRecord container,
@Nullable Rect fixedOrientationBounds) {
+ mOriginalRotation = display.getRotation();
mIsFloating = container.getWindowConfiguration().tasksAreFloating();
if (mIsFloating) {
final Rect containerBounds = container.getWindowConfiguration().getBounds();
@@ -8681,7 +8876,8 @@
outAppBounds.offset(insets.left, insets.top);
} else if (rotation != ROTATION_UNDEFINED) {
// Ensure the app bounds won't overlap with insets.
- Task.intersectWithInsetsIfFits(outAppBounds, outBounds, mNonDecorInsets[rotation]);
+ TaskFragment.intersectWithInsetsIfFits(outAppBounds, outBounds,
+ mNonDecorInsets[rotation]);
}
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java b/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
index 8540fa7..30c7b23 100644
--- a/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
+++ b/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
@@ -16,10 +16,10 @@
package com.android.server.wm;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
-import static com.android.server.wm.Task.ActivityState.PAUSING;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
import android.util.ArraySet;
import android.util.Slog;
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index e2ef82b..8a4c788 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -28,6 +28,7 @@
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -58,6 +59,7 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
@@ -74,7 +76,6 @@
import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_DISPLAY;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
import static com.android.server.wm.Task.REPARENT_MOVE_ROOT_TASK_TO_FRONT;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
@@ -1549,6 +1550,11 @@
newTransition.setRemoteTransition(remoteTransition);
}
mService.getTransitionController().collect(r);
+ // TODO(b/188669821): Remove when navbar reparenting moves to shell
+ if (r.getActivityType() == ACTIVITY_TYPE_HOME && r.getOptions() != null
+ && r.getOptions().getTransientLaunch()) {
+ mService.getTransitionController().setIsLegacyRecents();
+ }
try {
mService.deferWindowLayout();
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 5e75ceb..c582702 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1204,8 +1204,8 @@
// If this is coming from the currently resumed activity, it is
// effectively saying that app switches are allowed at this point.
final Task topFocusedRootTask = getTopDisplayFocusedRootTask();
- if (topFocusedRootTask != null && topFocusedRootTask.getResumedActivity() != null
- && topFocusedRootTask.getResumedActivity().info.applicationInfo.uid
+ if (topFocusedRootTask != null && topFocusedRootTask.getTopResumedActivity() != null
+ && topFocusedRootTask.getTopResumedActivity().info.applicationInfo.uid
== Binder.getCallingUid()) {
mAppSwitchesAllowed = true;
}
@@ -3727,6 +3727,20 @@
}
}
+ @Override
+ public void detachNavigationBarFromApp(@NonNull IBinder transition) {
+ mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
+ "detachNavigationBarFromApp");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ getTransitionController().legacyDetachNavigationBarFromApp(transition);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
void dumpLastANRLocked(PrintWriter pw) {
pw.println("ACTIVITY MANAGER LAST ANR (dumpsys activity lastanr)");
if (mLastANRState == null) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 7fe0f5b..fde02ec 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -48,6 +48,9 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESTARTING_PROCESS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_IDLE;
@@ -72,8 +75,6 @@
import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
-import static com.android.server.wm.Task.ActivityState.PAUSED;
-import static com.android.server.wm.Task.ActivityState.PAUSING;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK;
import static com.android.server.wm.Task.REPARENT_KEEP_ROOT_TASK_AT_FRONT;
import static com.android.server.wm.Task.TAG_CLEANUP;
@@ -136,7 +137,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.ReferrerIntent;
-import com.android.internal.os.TransferPipe;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.function.pooled.PooledConsumer;
@@ -146,7 +146,6 @@
import com.android.server.wm.ActivityMetricsLogger.LaunchingState;
import java.io.FileDescriptor;
-import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -1968,76 +1967,14 @@
static boolean dumpHistoryList(FileDescriptor fd, PrintWriter pw, List<ActivityRecord> list,
String prefix, String label, boolean complete, boolean brief, boolean client,
String dumpPackage, boolean needNL, Runnable header, Task lastTask) {
- String innerPrefix = null;
- String[] args = null;
boolean printed = false;
- for (int i=list.size()-1; i>=0; i--) {
+ for (int i = list.size() - 1; i >= 0; i--) {
final ActivityRecord r = list.get(i);
- if (dumpPackage != null && !dumpPackage.equals(r.packageName)) {
- continue;
- }
- if (innerPrefix == null) {
- innerPrefix = prefix + " ";
- args = new String[0];
- }
- printed = true;
- final boolean full = !brief && (complete || !r.isInHistory());
- if (needNL) {
- pw.println("");
- needNL = false;
- }
- if (header != null) {
- header.run();
- header = null;
- }
- if (lastTask != r.getTask()) {
- lastTask = r.getTask();
- pw.print(prefix);
- pw.print(full ? "* " : " ");
- pw.println(lastTask);
- if (full) {
- lastTask.dump(pw, prefix + " ");
- } else if (complete) {
- // Complete + brief == give a summary. Isn't that obvious?!?
- if (lastTask.intent != null) {
- pw.print(prefix); pw.print(" ");
- pw.println(lastTask.intent.toInsecureString());
- }
- }
- }
- pw.print(prefix); pw.print(full ? " * " : " "); pw.print(label);
- pw.print(" #"); pw.print(i); pw.print(": ");
- pw.println(r);
- if (full) {
- r.dump(pw, innerPrefix, true /* dumpAll */);
- } else if (complete) {
- // Complete + brief == give a summary. Isn't that obvious?!?
- pw.print(innerPrefix); pw.println(r.intent.toInsecureString());
- if (r.app != null) {
- pw.print(innerPrefix); pw.println(r.app);
- }
- }
- if (client && r.attachedToProcess()) {
- // flush anything that is already in the PrintWriter since the thread is going
- // to write to the file descriptor directly
- pw.flush();
- try {
- TransferPipe tp = new TransferPipe();
- try {
- r.app.getThread().dumpActivity(
- tp.getWriteFd(), r.appToken, innerPrefix, args);
- // Short timeout, since blocking here can deadlock with the application.
- tp.go(fd, 2000);
- } finally {
- tp.kill();
- }
- } catch (IOException e) {
- pw.println(innerPrefix + "Failure while dumping the activity: " + e);
- } catch (RemoteException e) {
- pw.println(innerPrefix + "Got a RemoteException while dumping the activity");
- }
- needNL = true;
- }
+ ActivityRecord.dumpActivity(fd, pw, i, r, prefix, label, complete, brief,
+ client, dumpPackage, needNL, header, lastTask);
+ lastTask = r.getTask();
+ header = null;
+ needNL = client && r.attachedToProcess();
}
return printed;
}
@@ -2064,7 +2001,7 @@
void updateTopResumedActivityIfNeeded() {
final ActivityRecord prevTopActivity = mTopResumedActivity;
final Task topRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask();
- if (topRootTask == null || topRootTask.getResumedActivity() == prevTopActivity) {
+ if (topRootTask == null || topRootTask.getTopResumedActivity() == prevTopActivity) {
if (mService.isSleepingLocked()) {
// There won't be a next resumed activity. The top process should still be updated
// according to the current top focused activity.
@@ -2086,7 +2023,7 @@
}
// Update the current top activity.
- mTopResumedActivity = topRootTask.getResumedActivity();
+ mTopResumedActivity = topRootTask.getTopResumedActivity();
scheduleTopResumedActivityStateIfNeeded();
mService.updateTopApp(mTopResumedActivity);
@@ -2393,8 +2330,7 @@
String processName = null;
int uid = 0;
synchronized (mService.mGlobalLock) {
- if (r.attachedToProcess()
- && r.isState(Task.ActivityState.RESTARTING_PROCESS)) {
+ if (r.attachedToProcess() && r.isState(RESTARTING_PROCESS)) {
processName = r.app.mName;
uid = r.app.mUid;
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index c1b287f..ac687dc 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -78,10 +78,7 @@
import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation;
import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenEnterAnimation;
import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenExitAnimation;
-import static com.android.internal.policy.TransitionAnimation.THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN;
-import static com.android.internal.policy.TransitionAnimation.THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
-import static com.android.internal.policy.TransitionAnimation.THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN;
-import static com.android.internal.policy.TransitionAnimation.THUMBNAIL_TRANSITION_EXIT_SCALE_UP;
+import static com.android.internal.policy.TransitionAnimation.prepareThumbnailAnimationWithDuration;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
import static com.android.server.wm.AppTransitionProto.APP_TRANSITION_STATE;
@@ -102,12 +99,7 @@
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Picture;
import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
import android.hardware.HardwareBuffer;
import android.os.Binder;
import android.os.Debug;
@@ -132,7 +124,6 @@
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.AnimationUtils;
-import android.view.animation.ClipRectAnimation;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
import android.view.animation.ScaleAnimation;
@@ -641,24 +632,6 @@
/**
* Prepares the specified animation with a standard duration, interpolator, etc.
*/
- Animation prepareThumbnailAnimationWithDuration(@Nullable Animation a, int appWidth,
- int appHeight, long duration, Interpolator interpolator) {
- if (a != null) {
- if (duration > 0) {
- a.setDuration(duration);
- }
- a.setFillAfter(true);
- if (interpolator != null) {
- a.setInterpolator(interpolator);
- }
- a.initialize(appWidth, appHeight, appWidth, appHeight);
- }
- return a;
- }
-
- /**
- * Prepares the specified animation with a standard duration, interpolator, etc.
- */
Animation prepareThumbnailAnimation(Animation a, int appWidth, int appHeight, int transit) {
// Pick the desired duration. If this is an inter-activity transition,
// it is the standard duration for that. Otherwise we use the longer
@@ -678,56 +651,16 @@
}
/**
- * Return the current thumbnail transition state.
- */
- int getThumbnailTransitionState(boolean enter) {
- if (enter) {
- if (mNextAppTransitionScaleUp) {
- return THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
- } else {
- return THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN;
- }
- } else {
- if (mNextAppTransitionScaleUp) {
- return THUMBNAIL_TRANSITION_EXIT_SCALE_UP;
- } else {
- return THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN;
- }
- }
- }
-
- /**
* Creates an overlay with a background color and a thumbnail for the cross profile apps
* animation.
*/
HardwareBuffer createCrossProfileAppsThumbnail(
@DrawableRes int thumbnailDrawableRes, Rect frame) {
- final int width = frame.width();
- final int height = frame.height();
-
- final Picture picture = new Picture();
- final Canvas canvas = picture.beginRecording(width, height);
- canvas.drawColor(Color.argb(0.6f, 0, 0, 0));
- final int thumbnailSize = mService.mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.cross_profile_apps_thumbnail_size);
- final Drawable drawable = mService.mContext.getDrawable(thumbnailDrawableRes);
- drawable.setBounds(
- (width - thumbnailSize) / 2,
- (height - thumbnailSize) / 2,
- (width + thumbnailSize) / 2,
- (height + thumbnailSize) / 2);
- drawable.setTint(mContext.getColor(android.R.color.white));
- drawable.draw(canvas);
- picture.endRecording();
-
- return Bitmap.createBitmap(picture).getHardwareBuffer();
+ return mTransitionAnimation.createCrossProfileAppsThumbnail(thumbnailDrawableRes, frame);
}
Animation createCrossProfileAppsThumbnailAnimationLocked(Rect appRect) {
- final Animation animation =
- mTransitionAnimation.loadCrossProfileAppThumbnailEnterAnimation();
- return prepareThumbnailAnimationWithDuration(animation, appRect.width(),
- appRect.height(), 0, null);
+ return mTransitionAnimation.createCrossProfileAppsThumbnailAnimationLocked(appRect);
}
/**
@@ -735,115 +668,14 @@
* when a thumbnail is specified with the pending animation override.
*/
Animation createThumbnailAspectScaleAnimationLocked(Rect appRect, @Nullable Rect contentInsets,
- HardwareBuffer thumbnailHeader, WindowContainer container, int uiMode,
- int orientation) {
- Animation a;
- final int thumbWidthI = thumbnailHeader.getWidth();
- final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
- final int thumbHeightI = thumbnailHeader.getHeight();
- final int appWidth = appRect.width();
-
- float scaleW = appWidth / thumbWidth;
- getNextAppTransitionStartRect(container, mTmpRect);
- final float fromX;
- float fromY;
- final float toX;
- float toY;
- final float pivotX;
- final float pivotY;
- if (shouldScaleDownThumbnailTransition(uiMode, orientation)) {
- fromX = mTmpRect.left;
- fromY = mTmpRect.top;
-
- // For the curved translate animation to work, the pivot points needs to be at the
- // same absolute position as the one from the real surface.
- toX = mTmpRect.width() / 2 * (scaleW - 1f) + appRect.left;
- toY = appRect.height() / 2 * (1 - 1 / scaleW) + appRect.top;
- pivotX = mTmpRect.width() / 2;
- pivotY = appRect.height() / 2 / scaleW;
- if (mGridLayoutRecentsEnabled) {
- // In the grid layout, the header is displayed above the thumbnail instead of
- // overlapping it.
- fromY -= thumbHeightI;
- toY -= thumbHeightI * scaleW;
- }
- } else {
- pivotX = 0;
- pivotY = 0;
- fromX = mTmpRect.left;
- fromY = mTmpRect.top;
- toX = appRect.left;
- toY = appRect.top;
- }
- final long duration = getAspectScaleDuration();
- final Interpolator interpolator = getAspectScaleInterpolator();
- if (mNextAppTransitionScaleUp) {
- // Animation up from the thumbnail to the full screen
- Animation scale = new ScaleAnimation(1f, scaleW, 1f, scaleW, pivotX, pivotY);
- scale.setInterpolator(interpolator);
- scale.setDuration(duration);
- Animation alpha = new AlphaAnimation(1f, 0f);
- alpha.setInterpolator(mThumbnailFadeOutInterpolator);
- alpha.setDuration(duration);
- Animation translate = createCurvedMotion(fromX, toX, fromY, toY);
- translate.setInterpolator(interpolator);
- translate.setDuration(duration);
-
- mTmpFromClipRect.set(0, 0, thumbWidthI, thumbHeightI);
- mTmpToClipRect.set(appRect);
-
- // Containing frame is in screen space, but we need the clip rect in the
- // app space.
- mTmpToClipRect.offsetTo(0, 0);
- mTmpToClipRect.right = (int) (mTmpToClipRect.right / scaleW);
- mTmpToClipRect.bottom = (int) (mTmpToClipRect.bottom / scaleW);
-
- if (contentInsets != null) {
- mTmpToClipRect.inset((int) (-contentInsets.left * scaleW),
- (int) (-contentInsets.top * scaleW),
- (int) (-contentInsets.right * scaleW),
- (int) (-contentInsets.bottom * scaleW));
- }
-
- Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
- clipAnim.setInterpolator(interpolator);
- clipAnim.setDuration(duration);
-
- // This AnimationSet uses the Interpolators assigned above.
- AnimationSet set = new AnimationSet(false);
- set.addAnimation(scale);
- if (!mGridLayoutRecentsEnabled) {
- // In the grid layout, the header should be shown for the whole animation.
- set.addAnimation(alpha);
- }
- set.addAnimation(translate);
- set.addAnimation(clipAnim);
- a = set;
- } else {
- // Animation down from the full screen to the thumbnail
- Animation scale = new ScaleAnimation(scaleW, 1f, scaleW, 1f, pivotX, pivotY);
- scale.setInterpolator(interpolator);
- scale.setDuration(duration);
- Animation alpha = new AlphaAnimation(0f, 1f);
- alpha.setInterpolator(mThumbnailFadeInInterpolator);
- alpha.setDuration(duration);
- Animation translate = createCurvedMotion(toX, fromX, toY, fromY);
- translate.setInterpolator(interpolator);
- translate.setDuration(duration);
-
- // This AnimationSet uses the Interpolators assigned above.
- AnimationSet set = new AnimationSet(false);
- set.addAnimation(scale);
- if (!mGridLayoutRecentsEnabled) {
- // In the grid layout, the header should be shown for the whole animation.
- set.addAnimation(alpha);
- }
- set.addAnimation(translate);
- a = set;
-
- }
- return prepareThumbnailAnimationWithDuration(a, appWidth, appRect.height(), 0,
- null);
+ HardwareBuffer thumbnailHeader, WindowContainer container, int orientation) {
+ AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(
+ container.hashCode());
+ return mTransitionAnimation.createThumbnailAspectScaleAnimationLocked(appRect,
+ contentInsets, thumbnailHeader, orientation, spec != null ? spec.rect : null,
+ mDefaultNextAppTransitionAnimationSpec != null
+ ? mDefaultNextAppTransitionAnimationSpec.rect : null,
+ mNextAppTransitionScaleUp);
}
private Animation createCurvedMotion(float fromX, float toX, float fromY, float toY) {
@@ -1043,7 +875,7 @@
+ "transit=%s Callers=%s",
a, appTransitionOldToString(transit), Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL) {
- a = mTransitionAnimation.createClipRevealAnimationLocked(
+ a = mTransitionAnimation.createClipRevealAnimationLockedCompat(
transit, enter, frame, displayFrame,
mDefaultNextAppTransitionAnimationSpec != null
? mDefaultNextAppTransitionAnimationSpec.rect : null);
@@ -1052,7 +884,7 @@
+ "transit=%s Callers=%s",
a, appTransitionOldToString(transit), Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_SCALE_UP) {
- a = mTransitionAnimation.createScaleUpAnimationLocked(transit, enter, frame,
+ a = mTransitionAnimation.createScaleUpAnimationLockedCompat(transit, enter, frame,
mDefaultNextAppTransitionAnimationSpec != null
? mDefaultNextAppTransitionAnimationSpec.rect : null);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
@@ -1064,8 +896,8 @@
mNextAppTransitionScaleUp =
(mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP);
final HardwareBuffer thumbnailHeader = getAppTransitionThumbnailHeader(container);
- a = mTransitionAnimation.createThumbnailEnterExitAnimationLocked(
- getThumbnailTransitionState(enter), frame, transit, thumbnailHeader,
+ a = mTransitionAnimation.createThumbnailEnterExitAnimationLockedCompat(enter,
+ mNextAppTransitionScaleUp, frame, transit, thumbnailHeader,
mDefaultNextAppTransitionAnimationSpec != null
? mDefaultNextAppTransitionAnimationSpec.rect : null);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
@@ -1080,9 +912,9 @@
(mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP);
AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(
container.hashCode());
- a = mTransitionAnimation.createAspectScaledThumbnailEnterExitAnimationLocked(
- getThumbnailTransitionState(enter), orientation, transit, frame,
- insets, surfaceInsets, stableInsets, freeform, spec != null ? spec.rect : null,
+ a = mTransitionAnimation.createAspectScaledThumbnailEnterExitAnimationLocked(enter,
+ mNextAppTransitionScaleUp, orientation, transit, frame, insets, surfaceInsets,
+ stableInsets, freeform, spec != null ? spec.rect : null,
mDefaultNextAppTransitionAnimationSpec != null
? mDefaultNextAppTransitionAnimationSpec.rect : null);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 8fbe177..2eeabf2 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -34,6 +34,7 @@
import static com.android.server.wm.ConfigurationContainerProto.OVERRIDE_CONFIGURATION;
import android.annotation.CallSuper;
+import android.annotation.NonNull;
import android.app.WindowConfiguration;
import android.content.res.Configuration;
import android.graphics.Point;
@@ -111,6 +112,7 @@
* This method should be used for getting settings applied in each particular level of the
* hierarchy.
*/
+ @NonNull
public Configuration getConfiguration() {
return mFullConfiguration;
}
@@ -170,11 +172,13 @@
}
/** Returns requested override configuration applied to this configuration container. */
+ @NonNull
public Configuration getRequestedOverrideConfiguration() {
return mRequestedOverrideConfiguration;
}
/** Returns the resolved override configuration. */
+ @NonNull
Configuration getResolvedOverrideConfiguration() {
return mResolvedOverrideConfiguration;
}
@@ -203,6 +207,7 @@
* Get merged override configuration from the top of the hierarchy down to this particular
* instance. This should be reported to client as override config.
*/
+ @NonNull
public Configuration getMergedOverrideConfiguration() {
return mMergedOverrideConfiguration;
}
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index b24ab93..86bbd1f 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -495,8 +495,10 @@
DisplayAreaInfo getDisplayAreaInfo() {
- DisplayAreaInfo info = new DisplayAreaInfo(mRemoteToken.toWindowContainerToken(),
+ final DisplayAreaInfo info = new DisplayAreaInfo(mRemoteToken.toWindowContainerToken(),
getDisplayContent().getDisplayId(), mFeatureId);
+ final RootDisplayArea root = getRootDisplayArea();
+ info.rootDisplayAreaId = root == null ? getDisplayContent().mFeatureId : root.mFeatureId;
info.configuration.setTo(getConfiguration());
return info;
}
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
index 47d7c9d..3d7ac6c 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
@@ -25,6 +25,7 @@
import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_LAST;
+import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID;
import android.annotation.Nullable;
import android.os.Bundle;
@@ -135,12 +136,6 @@
*/
class DisplayAreaPolicyBuilder {
- /**
- * Key to specify the {@link RootDisplayArea} to attach the window to. Should be used by the
- * function passed in from {@link #setSelectRootForWindowFunc(BiFunction)}
- */
- static final String KEY_ROOT_DISPLAY_AREA_ID = "root_display_area_id";
-
@Nullable private HierarchyBuilder mRootHierarchyBuilder;
private final ArrayList<HierarchyBuilder> mDisplayAreaGroupHierarchyBuilders =
new ArrayList<>();
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 81992d8..8dfaa6f 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -92,6 +92,7 @@
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.DisplayContentProto.APP_TRANSITION;
import static com.android.server.wm.DisplayContentProto.CLOSING_APPS;
import static com.android.server.wm.DisplayContentProto.CURRENT_FOCUS;
@@ -114,7 +115,6 @@
import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.DISPLAY_CONTENT;
@@ -1008,6 +1008,8 @@
mAppTransition = new AppTransition(mWmService.mContext, mWmService, this);
mAppTransition.registerListenerLocked(mWmService.mActivityManagerAppTransitionNotifier);
+ mAtmService.getTransitionController().registerLegacyListener(
+ mWmService.mActivityManagerAppTransitionNotifier);
mAppTransition.registerListenerLocked(mFixedRotationTransitionListener);
mAppTransitionController = new AppTransitionController(mWmService, this);
mUnknownAppVisibilityController = new UnknownAppVisibilityController(mWmService, this);
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 977df93..7f7c095 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -613,6 +613,8 @@
}
};
displayContent.mAppTransition.registerListenerLocked(mAppTransitionListener);
+ mService.mAtmService.getTransitionController().registerLegacyListener(
+ mAppTransitionListener);
mImmersiveModeConfirmation = new ImmersiveModeConfirmation(mContext, looper,
mService.mVrModeEnabled);
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index 316c20b..027156f 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -24,7 +24,7 @@
/** Helper class to ensure activities are in the right visible state for a container. */
class EnsureActivitiesVisibleHelper {
- private final Task mTask;
+ private final TaskFragment mTaskFragment;
private ActivityRecord mTop;
private ActivityRecord mStarting;
private boolean mAboveTop;
@@ -34,12 +34,12 @@
private boolean mPreserveWindows;
private boolean mNotifyClients;
- EnsureActivitiesVisibleHelper(Task container) {
- mTask = container;
+ EnsureActivitiesVisibleHelper(TaskFragment container) {
+ mTaskFragment = container;
}
/**
- * Update all attributes except {@link mTask} to use in subsequent calculations.
+ * Update all attributes except {@link mTaskFragment} to use in subsequent calculations.
*
* @param starting The activity that is being started
* @param configChanges Parts of the configuration that changed for this activity for evaluating
@@ -51,11 +51,11 @@
void reset(ActivityRecord starting, int configChanges, boolean preserveWindows,
boolean notifyClients) {
mStarting = starting;
- mTop = mTask.topRunningActivity();
+ mTop = mTaskFragment.topRunningActivity();
// If the top activity is not fullscreen, then we need to make sure any activities under it
// are now visible.
mAboveTop = mTop != null;
- mContainerShouldBeVisible = mTask.shouldBeVisible(mStarting);
+ mContainerShouldBeVisible = mTaskFragment.shouldBeVisible(mStarting);
mBehindFullscreenActivity = !mContainerShouldBeVisible;
mConfigChanges = configChanges;
mPreserveWindows = preserveWindows;
@@ -85,22 +85,23 @@
Slog.v(TAG_VISIBILITY, "ensureActivitiesVisible behind " + mTop
+ " configChanges=0x" + Integer.toHexString(configChanges));
}
- if (mTop != null) {
- mTask.checkTranslucentActivityWaiting(mTop);
+ if (mTop != null && mTaskFragment.asTask() != null) {
+ // TODO(14709632): Check if this needed to be implemented in TaskFragment.
+ mTaskFragment.asTask().checkTranslucentActivityWaiting(mTop);
}
// We should not resume activities that being launched behind because these
// activities are actually behind other fullscreen activities, but still required
// to be visible (such as performing Recents animation).
final boolean resumeTopActivity = mTop != null && !mTop.mLaunchTaskBehind
- && mTask.isTopActivityFocusable()
- && (starting == null || !starting.isDescendantOf(mTask));
+ && mTaskFragment.isTopActivityFocusable()
+ && (starting == null || !starting.isDescendantOf(mTaskFragment));
- mTask.forAllActivities(a -> {
+ mTaskFragment.forAllActivities(a -> {
setActivityVisibilityState(a, starting, resumeTopActivity);
});
- if (mTask.mAtmService.getTransitionController().getTransitionPlayer() != null) {
- mTask.getDisplayContent().mWallpaperController.adjustWallpaperWindows();
+ if (mTaskFragment.mAtmService.getTransitionController().getTransitionPlayer() != null) {
+ mTaskFragment.getDisplayContent().mWallpaperController.adjustWallpaperWindows();
}
}
@@ -179,9 +180,9 @@
r.makeInvisible();
}
- if (!mBehindFullscreenActivity && mTask.isActivityTypeHome() && r.isRootOfTask()) {
+ if (!mBehindFullscreenActivity && mTaskFragment.isActivityTypeHome() && r.isRootOfTask()) {
if (DEBUG_VISIBILITY) {
- Slog.v(TAG_VISIBILITY, "Home task: at " + mTask
+ Slog.v(TAG_VISIBILITY, "Home task: at " + mTaskFragment
+ " containerShouldBeVisible=" + mContainerShouldBeVisible
+ " behindFullscreenActivity=" + mBehindFullscreenActivity);
}
@@ -219,7 +220,8 @@
r.setVisibility(true);
}
if (r != starting) {
- mTask.mTaskSupervisor.startSpecificActivity(r, andResume, true /* checkConfig */);
+ mTaskFragment.mTaskSupervisor.startSpecificActivity(r, andResume,
+ true /* checkConfig */);
}
}
}
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index da47328..4f6a693 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -29,6 +29,7 @@
import static com.android.server.wm.WindowManagerService.H.UPDATE_MULTI_WINDOW_STACKS;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Trace;
import android.util.proto.ProtoOutputStream;
import android.view.InsetsSource;
@@ -90,6 +91,16 @@
onSourceChanged();
}
+ @Override
+ void updateControlForTarget(@Nullable InsetsControlTarget target, boolean force) {
+ if (target != null && target.getWindow() != null) {
+ // ime control target could be a different window.
+ // Refer WindowState#getImeControlTarget().
+ target = target.getWindow().getImeControlTarget();
+ }
+ super.updateControlForTarget(target, force);
+ }
+
private void onSourceChanged() {
if (mLastSource.equals(mSource)) {
return;
diff --git a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
index 747d365..f3b9cdf 100644
--- a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
+++ b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
@@ -20,6 +20,7 @@
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
+import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID;
import android.animation.ArgbEvaluator;
import android.animation.ValueAnimator;
@@ -420,7 +421,7 @@
}
final Bundle options = new Bundle();
- options.putInt(DisplayAreaPolicyBuilder.KEY_ROOT_DISPLAY_AREA_ID, rootDisplayAreaId);
+ options.putInt(KEY_ROOT_DISPLAY_AREA_ID, rootDisplayAreaId);
return options;
}
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 7daebff..cbd1314 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -307,11 +307,6 @@
// to control the window for now.
return;
}
- if (target != null && target.getWindow() != null) {
- // ime control target could be a different window.
- // Refer WindowState#getImeControlTarget().
- target = target.getWindow().getImeControlTarget();
- }
if (mWin != null && mWin.getSurfaceControl() == null) {
// if window doesn't have a surface, set it null and return.
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index a10b5d6..7cd9164 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -25,6 +25,8 @@
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
@@ -149,8 +151,7 @@
// Invisible activity should be stopped. If the recents activity is alive and its doesn't
// need to relaunch by current configuration, then it may be already in stopped state.
- if (!targetActivity.isState(Task.ActivityState.STOPPING,
- Task.ActivityState.STOPPED)) {
+ if (!targetActivity.isState(STOPPING, STOPPED)) {
// Add to stopping instead of stop immediately. So the client has the chance to perform
// traversal in non-stopped state (ViewRootImpl.mStopped) that would initialize more
// things (e.g. the measure can be done earlier). The actual stop will be performed when
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index ea80b8b..f8cae6b 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -52,6 +52,11 @@
import static com.android.server.policy.PhoneWindowManager.SYSTEM_DIALOG_REASON_ASSIST;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.ActivityRecord.State.FINISHING;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
@@ -69,14 +74,9 @@
import static com.android.server.wm.RootWindowContainerProto.IS_HOME_RECENTS_COMPONENT;
import static com.android.server.wm.RootWindowContainerProto.KEYGUARD_CONTROLLER;
import static com.android.server.wm.RootWindowContainerProto.WINDOW_CONTAINER;
-import static com.android.server.wm.Task.ActivityState.FINISHING;
-import static com.android.server.wm.Task.ActivityState.PAUSED;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
-import static com.android.server.wm.Task.ActivityState.STOPPED;
-import static com.android.server.wm.Task.ActivityState.STOPPING;
import static com.android.server.wm.Task.REPARENT_LEAVE_ROOT_TASK_IN_PLACE;
import static com.android.server.wm.Task.REPARENT_MOVE_ROOT_TASK_TO_FRONT;
-import static com.android.server.wm.Task.TASK_VISIBILITY_INVISIBLE;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_INVISIBLE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
@@ -1859,7 +1859,7 @@
if (focusedRootTask == null) {
return null;
}
- final ActivityRecord resumedActivity = focusedRootTask.getResumedActivity();
+ final ActivityRecord resumedActivity = focusedRootTask.getTopResumedActivity();
if (resumedActivity != null && resumedActivity.app != null) {
return resumedActivity;
}
@@ -1881,11 +1881,11 @@
// foreground.
WindowProcessController fgApp = getItemFromRootTasks(rootTask -> {
if (isTopDisplayFocusedRootTask(rootTask)) {
- final ActivityRecord resumedActivity = rootTask.getResumedActivity();
+ final ActivityRecord resumedActivity = rootTask.getTopResumedActivity();
if (resumedActivity != null) {
return resumedActivity.app;
- } else if (rootTask.getPausingActivity() != null) {
- return rootTask.getPausingActivity().app;
+ } else if (rootTask.getTopPausingActivity() != null) {
+ return rootTask.getTopPausingActivity().app;
}
}
return null;
@@ -1911,7 +1911,8 @@
return;
}
- if (rootTask.getVisibility(null /*starting*/) == TASK_VISIBILITY_INVISIBLE) {
+ if (rootTask.getVisibility(null /* starting */)
+ == TASK_FRAGMENT_VISIBILITY_INVISIBLE) {
return;
}
@@ -2376,7 +2377,9 @@
if (displayShouldSleep) {
rootTask.goToSleepIfPossible(false /* shuttingDown */);
} else {
- rootTask.awakeFromSleepingLocked();
+ rootTask.forAllLeafTaskFragments(
+ taskFragment -> taskFragment.awakeFromSleeping(),
+ true /* traverseTopToBottom */);
if (rootTask.isFocusedRootTaskOnDisplay()
&& !mTaskSupervisor.getKeyguardController()
.isKeyguardOrAodShowing(display.mDisplayId)) {
@@ -2736,8 +2739,8 @@
if (DEBUG_SWITCH) {
Slog.v(TAG_SWITCH, "Destroying " + r + " in state " + r.getState()
- + " resumed=" + r.getTask().getResumedActivity() + " pausing="
- + r.getTask().getPausingActivity() + " for reason "
+ + " resumed=" + r.getTask().getTopResumedActivity() + " pausing="
+ + r.getTask().getTopPausingActivity() + " for reason "
+ mDestroyAllActivitiesReason);
}
@@ -3324,7 +3327,7 @@
if (rootTask == null || !rootTask.hasActivity()) {
continue;
}
- final ActivityRecord resumedActivity = rootTask.getResumedActivity();
+ final ActivityRecord resumedActivity = rootTask.getTopResumedActivity();
if (resumedActivity == null || !resumedActivity.idle) {
ProtoLog.d(WM_DEBUG_STATES, "allResumedActivitiesIdle: rootTask=%d %s "
+ "not idle", rootTask.getRootTaskId(), resumedActivity);
@@ -3339,7 +3342,7 @@
boolean allResumedActivitiesVisible() {
boolean[] foundResumed = {false};
final boolean foundInvisibleResumedActivity = forAllRootTasks(rootTask -> {
- final ActivityRecord r = rootTask.getResumedActivity();
+ final ActivityRecord r = rootTask.getTopResumedActivity();
if (r != null) {
if (!r.nowVisible) {
return true;
@@ -3357,7 +3360,7 @@
boolean allPausedActivitiesComplete() {
boolean[] pausing = {true};
final boolean hasActivityNotCompleted = forAllLeafTasks(task -> {
- final ActivityRecord r = task.getPausingActivity();
+ final ActivityRecord r = task.getTopPausingActivity();
if (r != null && !r.isState(PAUSED, STOPPED, STOPPING, FINISHING)) {
ProtoLog.d(WM_DEBUG_STATES, "allPausedActivitiesComplete: "
+ "r=%s state=%s", r, r.getState());
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 7af8d8b..0fb9739 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -27,13 +27,10 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP;
-import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.activityTypeToString;
import static android.app.WindowConfiguration.windowingModeToString;
@@ -43,7 +40,6 @@
import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
-import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
@@ -53,9 +49,6 @@
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -65,7 +58,6 @@
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
-import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
@@ -83,21 +75,18 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
import static com.android.server.wm.ActivityRecord.STARTING_WINDOW_SHOWN;
+import static com.android.server.wm.ActivityRecord.State.INITIALIZING;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STARTED;
import static com.android.server.wm.ActivityRecord.TRANSFER_SPLASH_SCREEN_COPYING;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TRANSITION;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ADD_REMOVE;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_APP;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CLEANUP;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ROOT_TASK;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STATES;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TRANSITION;
@@ -110,7 +99,6 @@
import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS;
-import static com.android.server.wm.ActivityTaskSupervisor.dumpHistoryList;
import static com.android.server.wm.ActivityTaskSupervisor.printThisActivity;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
@@ -121,11 +109,6 @@
import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_PINNABLE;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
-import static com.android.server.wm.Task.ActivityState.PAUSED;
-import static com.android.server.wm.Task.ActivityState.PAUSING;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
-import static com.android.server.wm.Task.ActivityState.STARTED;
-import static com.android.server.wm.Task.ActivityState.STOPPING;
import static com.android.server.wm.TaskProto.ACTIVITY_TYPE;
import static com.android.server.wm.TaskProto.AFFINITY;
import static com.android.server.wm.TaskProto.BOUNDS;
@@ -168,14 +151,7 @@
import android.app.IActivityController;
import android.app.PictureInPictureParams;
import android.app.RemoteAction;
-import android.app.ResultInfo;
import android.app.TaskInfo;
-import android.app.WindowConfiguration;
-import android.app.servertransaction.ActivityResultItem;
-import android.app.servertransaction.ClientTransaction;
-import android.app.servertransaction.NewIntentItem;
-import android.app.servertransaction.PauseActivityItem;
-import android.app.servertransaction.ResumeActivityItem;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -244,23 +220,21 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
-import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
-class Task extends WindowContainer<WindowContainer> {
+/**
+ * {@link Task} is a TaskFragment that can contain a group of activities to perform a certain job.
+ * Activities of the same task affinities usually group in the same {@link Task}. A {@link Task}
+ * can also be an entity that showing in the Recents Screen for a job that user interacted with.
+ * A {@link Task} can also contain other {@link Task}s.
+ */
+class Task extends TaskFragment {
private static final String TAG = TAG_WITH_CLASS_NAME ? "Task" : TAG_ATM;
- static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
- private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
static final String TAG_TASKS = TAG + POSTFIX_TASKS;
- private static final String TAG_APP = TAG + POSTFIX_APP;
static final String TAG_CLEANUP = TAG + POSTFIX_CLEANUP;
- private static final String TAG_PAUSE = TAG + POSTFIX_PAUSE;
- private static final String TAG_RESULTS = TAG + POSTFIX_RESULTS;
- private static final String TAG_ROOT_TASK = TAG + POSTFIX_ROOT_TASK;
- private static final String TAG_STATES = TAG + POSTFIX_STATES;
private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
private static final String TAG_TRANSITION = TAG + POSTFIX_TRANSITION;
private static final String TAG_USER_LEAVING = TAG + POSTFIX_USER_LEAVING;
@@ -303,10 +277,6 @@
private static final String ATTR_LAST_SNAPSHOT_CONTENT_INSETS = "last_snapshot_content_insets";
private static final String ATTR_LAST_SNAPSHOT_BUFFER_SIZE = "last_snapshot_buffer_size";
- // Set to false to disable the preview that is shown while a new activity
- // is being started.
- private static final boolean SHOW_APP_STARTING_PREVIEW = true;
-
// How long to wait for all background Activities to redraw following a call to
// convertToTranslucent().
private static final long TRANSLUCENT_CONVERSION_TIMEOUT = 2000;
@@ -315,7 +285,6 @@
// code.
static final int PERSIST_TASK_VERSION = 1;
- static final int INVALID_MIN_SIZE = -1;
private float mShadowRadius = 0;
/**
@@ -335,36 +304,6 @@
// Do not move the root task as a part of reparenting
static final int REPARENT_LEAVE_ROOT_TASK_IN_PLACE = 2;
- @IntDef(prefix = {"TASK_VISIBILITY"}, value = {
- TASK_VISIBILITY_VISIBLE,
- TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
- TASK_VISIBILITY_INVISIBLE,
- })
- @interface TaskVisibility {}
-
- /** Task is visible. No other tasks on top that fully or partially occlude it. */
- static final int TASK_VISIBILITY_VISIBLE = 0;
-
- /** Task is partially occluded by other translucent task(s) on top of it. */
- static final int TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT = 1;
-
- /** Task is completely invisible. */
- static final int TASK_VISIBILITY_INVISIBLE = 2;
-
- enum ActivityState {
- INITIALIZING,
- STARTED,
- RESUMED,
- PAUSING,
- PAUSED,
- STOPPING,
- STOPPED,
- FINISHING,
- DESTROYING,
- DESTROYED,
- RESTARTING_PROCESS
- }
-
// The topmost Activity passed to convertToTranslucent(). When non-null it means we are
// waiting for all Activities in mUndrawnActivitiesBelowTopTranslucent to be removed as they
// are drawn. When the last member of mUndrawnActivitiesBelowTopTranslucent is removed the
@@ -446,7 +385,6 @@
CharSequence lastDescription; // Last description captured for this item.
- Task mAdjacentTask; // Task adjacent to this one.
int mAffiliatedTaskId; // taskId of parent affiliation or self if no parent.
Task mPrevAffiliate; // previous task in affiliated chain.
int mPrevAffiliateTaskId = INVALID_TASK_ID; // previous id for persistence.
@@ -458,21 +396,12 @@
String mCallingPackage;
String mCallingFeatureId;
- private final Rect mTmpStableBounds = new Rect();
- private final Rect mTmpNonDecorBounds = new Rect();
- private final Rect mTmpBounds = new Rect();
- private final Rect mTmpInsets = new Rect();
- private final Rect mTmpFullBounds = new Rect();
private static final Rect sTmpBounds = new Rect();
// Last non-fullscreen bounds the task was launched in or resized to.
// The information is persisted and used to determine the appropriate root task to launch the
// task into on restore.
Rect mLastNonFullscreenBounds = null;
- // Minimal width and height of this task when it's resizeable. -1 means it should use the
- // default minimal width/height.
- int mMinWidth;
- int mMinHeight;
// The surface transition of the target when recents animation is finished.
// This is originally introduced to carry out the current surface control position and window
@@ -497,10 +426,6 @@
/** Used by fillTaskInfo */
final TaskActivitiesReport mReuseActivitiesReport = new TaskActivitiesReport();
- final ActivityTaskManagerService mAtmService;
- final ActivityTaskSupervisor mTaskSupervisor;
- final RootWindowContainer mRootWindowContainer;
-
/* Unique identifier for this task. */
final int mTaskId;
/* User for which this task was created. */
@@ -571,29 +496,6 @@
/** ActivityRecords that are exiting, but still on screen for animations. */
final ArrayList<ActivityRecord> mExitingActivities = new ArrayList<>();
- /**
- * When we are in the process of pausing an activity, before starting the
- * next one, this variable holds the activity that is currently being paused.
- *
- * Only set at leaf tasks.
- */
- @Nullable
- private ActivityRecord mPausingActivity = null;
-
- /**
- * This is the last activity that we put into the paused state. This is
- * used to determine if we need to do an activity transition while sleeping,
- * when we normally hold the top activity paused.
- */
- ActivityRecord mLastPausedActivity = null;
-
- /**
- * Current activity that is resumed, or null if there is none.
- * Only set at leaf tasks.
- */
- @Nullable
- private ActivityRecord mResumedActivity = null;
-
private boolean mForceShowForAllUsers;
/** When set, will force the task to report as invisible. */
@@ -641,121 +543,6 @@
}
private static final ResetTargetTaskHelper sResetTargetTaskHelper = new ResetTargetTaskHelper();
- private final EnsureActivitiesVisibleHelper mEnsureActivitiesVisibleHelper =
- new EnsureActivitiesVisibleHelper(this);
- private final EnsureVisibleActivitiesConfigHelper mEnsureVisibleActivitiesConfigHelper =
- new EnsureVisibleActivitiesConfigHelper();
- private class EnsureVisibleActivitiesConfigHelper {
- private boolean mUpdateConfig;
- private boolean mPreserveWindow;
- private boolean mBehindFullscreen;
-
- void reset(boolean preserveWindow) {
- mPreserveWindow = preserveWindow;
- mUpdateConfig = false;
- mBehindFullscreen = false;
- }
-
- void process(ActivityRecord start, boolean preserveWindow) {
- if (start == null || !start.mVisibleRequested) {
- return;
- }
- reset(preserveWindow);
-
- final PooledFunction f = PooledLambda.obtainFunction(
- EnsureVisibleActivitiesConfigHelper::processActivity, this,
- PooledLambda.__(ActivityRecord.class));
- forAllActivities(f, start, true /*includeBoundary*/, true /*traverseTopToBottom*/);
- f.recycle();
-
- if (mUpdateConfig) {
- // Ensure the resumed state of the focus activity if we updated the configuration of
- // any activity.
- mRootWindowContainer.resumeFocusedTasksTopActivities();
- }
- }
-
- boolean processActivity(ActivityRecord r) {
- mUpdateConfig |= r.ensureActivityConfiguration(0 /*globalChanges*/, mPreserveWindow);
- mBehindFullscreen |= r.occludesParent();
- return mBehindFullscreen;
- }
- }
-
- private final CheckBehindFullscreenActivityHelper mCheckBehindFullscreenActivityHelper =
- new CheckBehindFullscreenActivityHelper();
- private class CheckBehindFullscreenActivityHelper {
- private boolean mAboveTop;
- private boolean mBehindFullscreenActivity;
- private ActivityRecord mToCheck;
- private Consumer<ActivityRecord> mHandleBehindFullscreenActivity;
- private boolean mHandlingOccluded;
-
- private void reset(ActivityRecord toCheck,
- Consumer<ActivityRecord> handleBehindFullscreenActivity) {
- mToCheck = toCheck;
- mHandleBehindFullscreenActivity = handleBehindFullscreenActivity;
- mAboveTop = true;
- mBehindFullscreenActivity = false;
-
- if (!shouldBeVisible(null)) {
- // The root task is not visible, so no activity in it should be displaying a
- // starting window. Mark all activities below top and behind fullscreen.
- mAboveTop = false;
- mBehindFullscreenActivity = true;
- }
-
- mHandlingOccluded = mToCheck == null && mHandleBehindFullscreenActivity != null;
- }
-
- boolean process(ActivityRecord toCheck,
- Consumer<ActivityRecord> handleBehindFullscreenActivity) {
- reset(toCheck, handleBehindFullscreenActivity);
-
- if (!mHandlingOccluded && mBehindFullscreenActivity) {
- return true;
- }
-
- final ActivityRecord topActivity = topRunningActivity();
- final PooledFunction f = PooledLambda.obtainFunction(
- CheckBehindFullscreenActivityHelper::processActivity, this,
- PooledLambda.__(ActivityRecord.class), topActivity);
- forAllActivities(f);
- f.recycle();
-
- return mBehindFullscreenActivity;
- }
-
- /** Returns {@code true} to stop the outer loop and indicate the result is computed. */
- private boolean processActivity(ActivityRecord r, ActivityRecord topActivity) {
- if (mAboveTop) {
- if (r == topActivity) {
- if (r == mToCheck) {
- // It is the top activity in a visible root task.
- mBehindFullscreenActivity = false;
- return true;
- }
- mAboveTop = false;
- }
- mBehindFullscreenActivity |= r.occludesParent();
- return false;
- }
-
- if (mHandlingOccluded) {
- // Iterating through all occluded activities.
- if (mBehindFullscreenActivity) {
- mHandleBehindFullscreenActivity.accept(r);
- }
- } else if (r == mToCheck) {
- return true;
- } else if (mBehindFullscreenActivity) {
- // It is occluded before {@param toCheck} is found.
- return true;
- }
- mBehindFullscreenActivity |= r.occludesParent();
- return false;
- }
- }
private final FindRootHelper mFindRootHelper = new FindRootHelper();
private class FindRootHelper {
@@ -858,11 +645,8 @@
IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor,
boolean _createdByOrganizer, IBinder _launchCookie, boolean _deferTaskAppear,
boolean _removeWithTaskOrganizer) {
- super(atmService.mWindowManager);
+ super(atmService);
- mAtmService = atmService;
- mTaskSupervisor = atmService.mTaskSupervisor;
- mRootWindowContainer = mAtmService.mRootWindowContainer;
mTaskId = _taskId;
mUserId = _userId;
mResizeMode = resizeMode;
@@ -1475,64 +1259,54 @@
}
}
- void cleanUpActivityReferences(ActivityRecord r) {
- // mPausingActivity is set at leaf task
- if (mPausingActivity != null && mPausingActivity == r) {
- mPausingActivity = null;
- }
-
- if (mResumedActivity != null && mResumedActivity == r) {
- setResumedActivity(null, "cleanUpActivityReferences");
- }
-
- final WindowContainer parent = getParent();
- if (parent != null && parent.asTask() != null) {
- parent.asTask().cleanUpActivityReferences(r);
- return;
- }
- r.removeTimeouts();
- mExitingActivities.remove(r);
- }
-
- /** @return the currently resumed activity. */
- ActivityRecord getResumedActivity() {
+ /** Returns the currently topmost resumed activity. */
+ @Nullable
+ ActivityRecord getTopResumedActivity() {
if (isLeafTask()) {
- return mResumedActivity;
+ final ActivityRecord[] resumedActivity = new ActivityRecord[1];
+ forAllLeafTaskFragments(fragment -> {
+ if (fragment.getResumedActivity() != null) {
+ resumedActivity[0] = fragment.getResumedActivity();
+ return true;
+ }
+ return false;
+ });
+ return resumedActivity[0];
}
- final Task task = getTask(t -> t.mResumedActivity != null, true /* traverseTopToBottom */);
- return task != null ? task.mResumedActivity : null;
- }
-
- @VisibleForTesting
- void setPausingActivity(ActivityRecord pausing) {
- mPausingActivity = pausing;
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ ActivityRecord resumedActivity = mChildren.get(i).asTask().getTopResumedActivity();
+ if (resumedActivity != null) {
+ return resumedActivity;
+ }
+ }
+ return null;
}
/**
- * @return the currently pausing activity of this task or the topmost pausing activity of the
- * child tasks
+ * Returns the currently topmost pausing activity.
*/
- ActivityRecord getPausingActivity() {
+ @Nullable
+ ActivityRecord getTopPausingActivity() {
if (isLeafTask()) {
- return mPausingActivity;
+ final ActivityRecord[] pausingActivity = new ActivityRecord[1];
+ forAllLeafTaskFragments(fragment -> {
+ if (fragment.getPausingActivity() != null) {
+ pausingActivity[0] = fragment.getPausingActivity();
+ return true;
+ }
+ return false;
+ });
+ return pausingActivity[0];
}
- final Task task = getTask(t -> t.mPausingActivity != null, true /* traverseTopToBottom */);
- return task != null ? task.mPausingActivity : null;
- }
-
- void setResumedActivity(ActivityRecord r, String reason) {
- warnForNonLeafTask("setResumedActivity");
- if (mResumedActivity == r) {
- return;
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ ActivityRecord pausingActivity = mChildren.get(i).asTask().getTopPausingActivity();
+ if (pausingActivity != null) {
+ return pausingActivity;
+ }
}
-
- if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) Slog.d(TAG_ROOT_TASK,
- "setResumedActivity task:" + this + " + from: "
- + mResumedActivity + " to:" + r + " reason:" + reason);
- mResumedActivity = r;
- mTaskSupervisor.updateTopResumedActivityIfNeeded();
+ return null;
}
void updateTaskMovement(boolean toTop, int position) {
@@ -1571,11 +1345,6 @@
mTaskId, mUserId);
}
- void setAdjacentTask(Task adjacent) {
- mAdjacentTask = adjacent;
- adjacent.mAdjacentTask = this;
- }
-
void setTaskToAffiliateWith(Task taskToAffiliateWith) {
closeRecentsChain();
mAffiliatedTaskId = taskToAffiliateWith.mAffiliatedTaskId;
@@ -1684,15 +1453,6 @@
}
@Override
- public int getActivityType() {
- final int applicationType = super.getActivityType();
- if (applicationType != ACTIVITY_TYPE_UNDEFINED || !hasChild()) {
- return applicationType;
- }
- return getTopChild().getActivityType();
- }
-
- @Override
void addChild(WindowContainer child, int index) {
// If this task had any child before we added this one.
boolean hadChild = hasChild();
@@ -1994,32 +1754,6 @@
&& supportsMultiWindowInDisplayArea(tda);
}
- boolean supportsMultiWindow() {
- return supportsMultiWindowInDisplayArea(getDisplayArea());
- }
-
- /**
- * @return whether this task supports multi-window if it is in the given
- * {@link TaskDisplayArea}.
- */
- boolean supportsMultiWindowInDisplayArea(@Nullable TaskDisplayArea tda) {
- if (!mAtmService.mSupportsMultiWindow) {
- return false;
- }
- if (tda == null) {
- Slog.w(TAG_TASKS, "Can't find TaskDisplayArea to determine support for multi"
- + " window. Task id=" + mTaskId + " attached=" + isAttached());
- return false;
- }
-
- if (!isResizeable() && !tda.supportsNonResizableMultiWindow()) {
- // Not support non-resizable in multi window.
- return false;
- }
-
- return tda.supportsActivityMinWidthHeightMultiWindow(mMinWidth, mMinHeight);
- }
-
/**
* Check whether this task can be launched on the specified display.
*
@@ -2155,60 +1889,6 @@
}
}
- void adjustForMinimalTaskDimensions(@NonNull Rect bounds, @NonNull Rect previousBounds,
- @NonNull Configuration parentConfig) {
- int minWidth = mMinWidth;
- int minHeight = mMinHeight;
- // If the task has no requested minimal size, we'd like to enforce a minimal size
- // so that the user can not render the task too small to manipulate. We don't need
- // to do this for the root pinned task as the bounds are controlled by the system.
- if (!inPinnedWindowingMode()) {
- final int defaultMinSizeDp = mRootWindowContainer.mDefaultMinSizeOfResizeableTaskDp;
- final float density = (float) parentConfig.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
- final int defaultMinSize = (int) (defaultMinSizeDp * density);
-
- if (minWidth == INVALID_MIN_SIZE) {
- minWidth = defaultMinSize;
- }
- if (minHeight == INVALID_MIN_SIZE) {
- minHeight = defaultMinSize;
- }
- }
- if (bounds.isEmpty()) {
- // If inheriting parent bounds, check if parent bounds adhere to minimum size. If they
- // do, we can just skip.
- final Rect parentBounds = parentConfig.windowConfiguration.getBounds();
- if (parentBounds.width() >= minWidth && parentBounds.height() >= minHeight) {
- return;
- }
- bounds.set(parentBounds);
- }
- final boolean adjustWidth = minWidth > bounds.width();
- final boolean adjustHeight = minHeight > bounds.height();
- if (!(adjustWidth || adjustHeight)) {
- return;
- }
-
- if (adjustWidth) {
- if (!previousBounds.isEmpty() && bounds.right == previousBounds.right) {
- bounds.left = bounds.right - minWidth;
- } else {
- // Either left bounds match, or neither match, or the previous bounds were
- // fullscreen and we default to keeping left.
- bounds.right = bounds.left + minWidth;
- }
- }
- if (adjustHeight) {
- if (!previousBounds.isEmpty() && bounds.bottom == previousBounds.bottom) {
- bounds.top = bounds.bottom - minHeight;
- } else {
- // Either top bounds match, or neither match, or the previous bounds were
- // fullscreen and we default to keeping top.
- bounds.bottom = bounds.top + minHeight;
- }
- }
- }
-
void setLastNonFullscreenBounds(Rect bounds) {
if (mLastNonFullscreenBounds == null) {
mLastNonFullscreenBounds = new Rect(bounds);
@@ -2217,32 +1897,6 @@
}
}
- /**
- * This should be called when an child activity changes state. This should only
- * be called from
- * {@link ActivityRecord#setState(ActivityState, String)} .
- * @param record The {@link ActivityRecord} whose state has changed.
- * @param state The new state.
- * @param reason The reason for the change.
- */
- void onActivityStateChanged(ActivityRecord record, ActivityState state, String reason) {
- warnForNonLeafTask("onActivityStateChanged");
- if (record == mResumedActivity && state != RESUMED) {
- setResumedActivity(null, reason + " - onActivityStateChanged");
- }
-
- if (state == RESUMED) {
- if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) {
- Slog.v(TAG_ROOT_TASK, "set resumed activity to:" + record + " reason:" + reason);
- }
- setResumedActivity(record, reason + " - onActivityStateChanged");
- if (record == mRootWindowContainer.getTopResumedActivity()) {
- mAtmService.setResumedActivityUncheckLocked(record, reason);
- }
- mTaskSupervisor.mRecentTasks.add(record.getTask());
- }
- }
-
private void onConfigurationChangedInner(Configuration newParentConfig) {
// Check if the new configuration supports persistent bounds (eg. is Freeform) and if so
// restore the last recorded non-fullscreen bounds.
@@ -2535,400 +2189,6 @@
mTaskSupervisor.mLaunchParamsPersister.saveTask(this, display);
}
- /**
- * Adjust bounds to stay within root task bounds.
- *
- * Since bounds might be outside of root task bounds, this method tries to move the bounds in
- * a way that keep them unchanged, but be contained within the root task bounds.
- *
- * @param bounds Bounds to be adjusted.
- * @param rootTaskBounds Bounds within which the other bounds should remain.
- * @param overlapPxX The amount of px required to be visible in the X dimension.
- * @param overlapPxY The amount of px required to be visible in the Y dimension.
- */
- private static void fitWithinBounds(Rect bounds, Rect rootTaskBounds, int overlapPxX,
- int overlapPxY) {
- if (rootTaskBounds == null || rootTaskBounds.isEmpty() || rootTaskBounds.contains(bounds)) {
- return;
- }
-
- // For each side of the parent (eg. left), check if the opposing side of the window (eg.
- // right) is at least overlap pixels away. If less, offset the window by that difference.
- int horizontalDiff = 0;
- // If window is smaller than overlap, use it's smallest dimension instead
- int overlapLR = Math.min(overlapPxX, bounds.width());
- if (bounds.right < (rootTaskBounds.left + overlapLR)) {
- horizontalDiff = overlapLR - (bounds.right - rootTaskBounds.left);
- } else if (bounds.left > (rootTaskBounds.right - overlapLR)) {
- horizontalDiff = -(overlapLR - (rootTaskBounds.right - bounds.left));
- }
- int verticalDiff = 0;
- int overlapTB = Math.min(overlapPxY, bounds.width());
- if (bounds.bottom < (rootTaskBounds.top + overlapTB)) {
- verticalDiff = overlapTB - (bounds.bottom - rootTaskBounds.top);
- } else if (bounds.top > (rootTaskBounds.bottom - overlapTB)) {
- verticalDiff = -(overlapTB - (rootTaskBounds.bottom - bounds.top));
- }
- bounds.offset(horizontalDiff, verticalDiff);
- }
-
- /**
- * Intersects inOutBounds with intersectBounds-intersectInsets. If inOutBounds is larger than
- * intersectBounds on a side, then the respective side will not be intersected.
- *
- * The assumption is that if inOutBounds is initially larger than intersectBounds, then the
- * inset on that side is no-longer applicable. This scenario happens when a task's minimal
- * bounds are larger than the provided parent/display bounds.
- *
- * @param inOutBounds the bounds to intersect.
- * @param intersectBounds the bounds to intersect with.
- * @param intersectInsets insets to apply to intersectBounds before intersecting.
- */
- static void intersectWithInsetsIfFits(
- Rect inOutBounds, Rect intersectBounds, Rect intersectInsets) {
- if (inOutBounds.right <= intersectBounds.right) {
- inOutBounds.right =
- Math.min(intersectBounds.right - intersectInsets.right, inOutBounds.right);
- }
- if (inOutBounds.bottom <= intersectBounds.bottom) {
- inOutBounds.bottom =
- Math.min(intersectBounds.bottom - intersectInsets.bottom, inOutBounds.bottom);
- }
- if (inOutBounds.left >= intersectBounds.left) {
- inOutBounds.left =
- Math.max(intersectBounds.left + intersectInsets.left, inOutBounds.left);
- }
- if (inOutBounds.top >= intersectBounds.top) {
- inOutBounds.top =
- Math.max(intersectBounds.top + intersectInsets.top, inOutBounds.top);
- }
- }
-
- /**
- * Gets bounds with non-decor and stable insets applied respectively.
- *
- * If bounds overhangs the display, those edges will not get insets. See
- * {@link #intersectWithInsetsIfFits}
- *
- * @param outNonDecorBounds where to place bounds with non-decor insets applied.
- * @param outStableBounds where to place bounds with stable insets applied.
- * @param bounds the bounds to inset.
- */
- private void calculateInsetFrames(Rect outNonDecorBounds, Rect outStableBounds, Rect bounds,
- DisplayInfo displayInfo) {
- outNonDecorBounds.set(bounds);
- outStableBounds.set(bounds);
- final Task rootTask = getRootTask();
- if (rootTask == null || rootTask.mDisplayContent == null) {
- return;
- }
- mTmpBounds.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
-
- final DisplayPolicy policy = rootTask.mDisplayContent.getDisplayPolicy();
- policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth,
- displayInfo.logicalHeight, displayInfo.displayCutout, mTmpInsets);
- intersectWithInsetsIfFits(outNonDecorBounds, mTmpBounds, mTmpInsets);
-
- policy.convertNonDecorInsetsToStableInsets(mTmpInsets, displayInfo.rotation);
- intersectWithInsetsIfFits(outStableBounds, mTmpBounds, mTmpInsets);
- }
-
- /**
- * Forces the app bounds related configuration can be computed by
- * {@link #computeConfigResourceOverrides(Configuration, Configuration, DisplayInfo,
- * ActivityRecord.CompatDisplayInsets)}.
- */
- private static void invalidateAppBoundsConfig(@NonNull Configuration inOutConfig) {
- final Rect appBounds = inOutConfig.windowConfiguration.getAppBounds();
- if (appBounds != null) {
- appBounds.setEmpty();
- }
- inOutConfig.screenWidthDp = Configuration.SCREEN_WIDTH_DP_UNDEFINED;
- inOutConfig.screenHeightDp = Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
- }
-
- void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
- @NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo) {
- if (overrideDisplayInfo != null) {
- // Make sure the screen related configs can be computed by the provided display info.
- inOutConfig.screenLayout = Configuration.SCREENLAYOUT_UNDEFINED;
- invalidateAppBoundsConfig(inOutConfig);
- }
- computeConfigResourceOverrides(inOutConfig, parentConfig, overrideDisplayInfo,
- null /* compatInsets */);
- }
-
- void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
- @NonNull Configuration parentConfig) {
- computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */,
- null /* compatInsets */);
- }
-
- void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
- @NonNull Configuration parentConfig,
- @Nullable ActivityRecord.CompatDisplayInsets compatInsets) {
- if (compatInsets != null) {
- // Make sure the app bounds can be computed by the compat insets.
- invalidateAppBoundsConfig(inOutConfig);
- }
- computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */,
- compatInsets);
- }
-
- /**
- * Calculates configuration values used by the client to get resources. This should be run
- * using app-facing bounds (bounds unmodified by animations or transient interactions).
- *
- * This assumes bounds are non-empty/null. For the null-bounds case, the caller is likely
- * configuring an "inherit-bounds" window which means that all configuration settings would
- * just be inherited from the parent configuration.
- **/
- void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
- @NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo,
- @Nullable ActivityRecord.CompatDisplayInsets compatInsets) {
- int windowingMode = inOutConfig.windowConfiguration.getWindowingMode();
- if (windowingMode == WINDOWING_MODE_UNDEFINED) {
- windowingMode = parentConfig.windowConfiguration.getWindowingMode();
- }
-
- float density = inOutConfig.densityDpi;
- if (density == Configuration.DENSITY_DPI_UNDEFINED) {
- density = parentConfig.densityDpi;
- }
- density *= DisplayMetrics.DENSITY_DEFAULT_SCALE;
-
- // The bounds may have been overridden at this level. If the parent cannot cover these
- // bounds, the configuration is still computed according to the override bounds.
- final boolean insideParentBounds;
-
- final Rect parentBounds = parentConfig.windowConfiguration.getBounds();
- final Rect resolvedBounds = inOutConfig.windowConfiguration.getBounds();
- if (resolvedBounds == null || resolvedBounds.isEmpty()) {
- mTmpFullBounds.set(parentBounds);
- insideParentBounds = true;
- } else {
- mTmpFullBounds.set(resolvedBounds);
- insideParentBounds = parentBounds.contains(resolvedBounds);
- }
-
- // Non-null compatibility insets means the activity prefers to keep its original size, so
- // out bounds doesn't need to be restricted by the parent or current display
- final boolean customContainerPolicy = compatInsets != null;
-
- Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
- if (outAppBounds == null || outAppBounds.isEmpty()) {
- // App-bounds hasn't been overridden, so calculate a value for it.
- inOutConfig.windowConfiguration.setAppBounds(mTmpFullBounds);
- outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
-
- if (!customContainerPolicy && windowingMode != WINDOWING_MODE_FREEFORM) {
- final Rect containingAppBounds;
- if (insideParentBounds) {
- containingAppBounds = parentConfig.windowConfiguration.getAppBounds();
- } else {
- // Restrict appBounds to display non-decor rather than parent because the
- // override bounds are beyond the parent. Otherwise, it won't match the
- // overridden bounds.
- final TaskDisplayArea displayArea = getDisplayArea();
- containingAppBounds = displayArea != null
- ? displayArea.getWindowConfiguration().getAppBounds() : null;
- }
- if (containingAppBounds != null && !containingAppBounds.isEmpty()) {
- outAppBounds.intersect(containingAppBounds);
- }
- }
- }
-
- if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED
- || inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
- if (!customContainerPolicy && WindowConfiguration.isFloating(windowingMode)) {
- mTmpNonDecorBounds.set(mTmpFullBounds);
- mTmpStableBounds.set(mTmpFullBounds);
- } else if (!customContainerPolicy
- && (overrideDisplayInfo != null || getDisplayContent() != null)) {
- final DisplayInfo di = overrideDisplayInfo != null
- ? overrideDisplayInfo
- : getDisplayContent().getDisplayInfo();
-
- // For calculating screenWidthDp, screenWidthDp, we use the stable inset screen
- // area, i.e. the screen area without the system bars.
- // The non decor inset are areas that could never be removed in Honeycomb. See
- // {@link WindowManagerPolicy#getNonDecorInsetsLw}.
- calculateInsetFrames(mTmpNonDecorBounds, mTmpStableBounds, mTmpFullBounds, di);
- } else {
- // Apply the given non-decor and stable insets to calculate the corresponding bounds
- // for screen size of configuration.
- int rotation = inOutConfig.windowConfiguration.getRotation();
- if (rotation == ROTATION_UNDEFINED) {
- rotation = parentConfig.windowConfiguration.getRotation();
- }
- if (rotation != ROTATION_UNDEFINED && customContainerPolicy) {
- mTmpNonDecorBounds.set(mTmpFullBounds);
- mTmpStableBounds.set(mTmpFullBounds);
- compatInsets.getBoundsByRotation(mTmpBounds, rotation);
- intersectWithInsetsIfFits(mTmpNonDecorBounds, mTmpBounds,
- compatInsets.mNonDecorInsets[rotation]);
- intersectWithInsetsIfFits(mTmpStableBounds, mTmpBounds,
- compatInsets.mStableInsets[rotation]);
- outAppBounds.set(mTmpNonDecorBounds);
- } else {
- // Set to app bounds because it excludes decor insets.
- mTmpNonDecorBounds.set(outAppBounds);
- mTmpStableBounds.set(outAppBounds);
- }
- }
-
- if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
- final int overrideScreenWidthDp = (int) (mTmpStableBounds.width() / density);
- inOutConfig.screenWidthDp = (insideParentBounds && !customContainerPolicy)
- ? Math.min(overrideScreenWidthDp, parentConfig.screenWidthDp)
- : overrideScreenWidthDp;
- }
- if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
- final int overrideScreenHeightDp = (int) (mTmpStableBounds.height() / density);
- inOutConfig.screenHeightDp = (insideParentBounds && !customContainerPolicy)
- ? Math.min(overrideScreenHeightDp, parentConfig.screenHeightDp)
- : overrideScreenHeightDp;
- }
-
- if (inOutConfig.smallestScreenWidthDp
- == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
- if (WindowConfiguration.isFloating(windowingMode)) {
- // For floating tasks, calculate the smallest width from the bounds of the task
- inOutConfig.smallestScreenWidthDp = (int) (
- Math.min(mTmpFullBounds.width(), mTmpFullBounds.height()) / density);
- }
- // otherwise, it will just inherit
- }
- }
-
- if (inOutConfig.orientation == ORIENTATION_UNDEFINED) {
- inOutConfig.orientation = (inOutConfig.screenWidthDp <= inOutConfig.screenHeightDp)
- ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
- }
- if (inOutConfig.screenLayout == Configuration.SCREENLAYOUT_UNDEFINED) {
- // For calculating screen layout, we need to use the non-decor inset screen area for the
- // calculation for compatibility reasons, i.e. screen area without system bars that
- // could never go away in Honeycomb.
- int compatScreenWidthDp = (int) (mTmpNonDecorBounds.width() / density);
- int compatScreenHeightDp = (int) (mTmpNonDecorBounds.height() / density);
- // Use overrides if provided. If both overrides are provided, mTmpNonDecorBounds is
- // undefined so it can't be used.
- if (inOutConfig.screenWidthDp != Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
- compatScreenWidthDp = inOutConfig.screenWidthDp;
- }
- if (inOutConfig.screenHeightDp != Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
- compatScreenHeightDp = inOutConfig.screenHeightDp;
- }
- // Reducing the screen layout starting from its parent config.
- inOutConfig.screenLayout = computeScreenLayoutOverride(parentConfig.screenLayout,
- compatScreenWidthDp, compatScreenHeightDp);
- }
- }
-
- /** Computes LONG, SIZE and COMPAT parts of {@link Configuration#screenLayout}. */
- static int computeScreenLayoutOverride(int sourceScreenLayout, int screenWidthDp,
- int screenHeightDp) {
- sourceScreenLayout = sourceScreenLayout
- & (Configuration.SCREENLAYOUT_LONG_MASK | Configuration.SCREENLAYOUT_SIZE_MASK);
- final int longSize = Math.max(screenWidthDp, screenHeightDp);
- final int shortSize = Math.min(screenWidthDp, screenHeightDp);
- return Configuration.reduceScreenLayout(sourceScreenLayout, longSize, shortSize);
- }
-
- @Override
- void resolveOverrideConfiguration(Configuration newParentConfig) {
- mTmpBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds());
- super.resolveOverrideConfiguration(newParentConfig);
-
- int windowingMode =
- getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
- final int parentWindowingMode = newParentConfig.windowConfiguration.getWindowingMode();
-
- // Resolve override windowing mode to fullscreen for home task (even on freeform
- // display), or split-screen if in split-screen mode.
- if (getActivityType() == ACTIVITY_TYPE_HOME && windowingMode == WINDOWING_MODE_UNDEFINED) {
- windowingMode = WindowConfiguration.isSplitScreenWindowingMode(parentWindowingMode)
- ? parentWindowingMode : WINDOWING_MODE_FULLSCREEN;
- getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
- }
-
- // Do not allow tasks not support multi window to be in a multi-window mode, unless it is in
- // pinned windowing mode.
- if (!supportsMultiWindow()) {
- final int candidateWindowingMode =
- windowingMode != WINDOWING_MODE_UNDEFINED ? windowingMode : parentWindowingMode;
- if (WindowConfiguration.inMultiWindowMode(candidateWindowingMode)
- && candidateWindowingMode != WINDOWING_MODE_PINNED) {
- getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(
- WINDOWING_MODE_FULLSCREEN);
- }
- }
-
- if (isLeafTask()) {
- resolveLeafOnlyOverrideConfigs(newParentConfig, mTmpBounds /* previousBounds */);
- }
- computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig);
- }
-
- private void resolveLeafOnlyOverrideConfigs(Configuration newParentConfig,
- Rect previousBounds) {
-
- int windowingMode =
- getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
- if (windowingMode == WINDOWING_MODE_UNDEFINED) {
- windowingMode = newParentConfig.windowConfiguration.getWindowingMode();
- }
- // Commit the resolved windowing mode so the canSpecifyOrientation won't get the old
- // mode that may cause the bounds to be miscalculated, e.g. letterboxed.
- getConfiguration().windowConfiguration.setWindowingMode(windowingMode);
- Rect outOverrideBounds =
- getResolvedOverrideConfiguration().windowConfiguration.getBounds();
-
- if (windowingMode == WINDOWING_MODE_FULLSCREEN) {
- // Use empty bounds to indicate "fill parent".
- outOverrideBounds.setEmpty();
- // The bounds for fullscreen mode shouldn't be adjusted by minimal size. Otherwise if
- // the parent or display is smaller than the size, the content may be cropped.
- return;
- }
-
- adjustForMinimalTaskDimensions(outOverrideBounds, previousBounds, newParentConfig);
- if (windowingMode == WINDOWING_MODE_FREEFORM) {
- computeFreeformBounds(outOverrideBounds, newParentConfig);
- return;
- }
- }
-
- /** Computes bounds for {@link WindowConfiguration#WINDOWING_MODE_FREEFORM}. */
- private void computeFreeformBounds(@NonNull Rect outBounds,
- @NonNull Configuration newParentConfig) {
- // by policy, make sure the window remains within parent somewhere
- final float density =
- ((float) newParentConfig.densityDpi) / DisplayMetrics.DENSITY_DEFAULT;
- final Rect parentBounds =
- new Rect(newParentConfig.windowConfiguration.getBounds());
- final DisplayContent display = getDisplayContent();
- if (display != null) {
- // If a freeform window moves below system bar, there is no way to move it again
- // by touch. Because its caption is covered by system bar. So we exclude them
- // from root task bounds. and then caption will be shown inside stable area.
- final Rect stableBounds = new Rect();
- display.getStableRect(stableBounds);
- parentBounds.intersect(stableBounds);
- }
-
- fitWithinBounds(outBounds, parentBounds,
- (int) (density * WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP),
- (int) (density * WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP));
-
- // Prevent to overlap caption with stable insets.
- final int offsetTop = parentBounds.top - outBounds.top;
- if (offsetTop > 0) {
- outBounds.offset(0, offsetTop);
- }
- }
-
Rect updateOverrideConfigurationFromLaunchBounds() {
// If the task is controlled by another organized task, do not set override
// configurations and let its parent (organized task) to control it;
@@ -2987,12 +2247,9 @@
return getRootTask().mTaskId;
}
+ @Nullable
Task getRootTask() {
- final WindowContainer parent = getParent();
- if (parent == null) return this;
-
- final Task parentTask = parent.asTask();
- return parentTask == null ? this : parentTask.getRootTask();
+ return getRootTaskFragment().asTask();
}
/** @return the first organized task. */
@@ -3107,12 +2364,12 @@
// and focused application if needed.
focusableTask.moveToFront(myReason);
// Top display focused root task is changed, update top resumed activity if needed.
- if (rootTask.getResumedActivity() != null) {
+ if (rootTask.getTopResumedActivity() != null) {
mTaskSupervisor.updateTopResumedActivityIfNeeded();
// Set focused app directly because if the next focused activity is already resumed
// (e.g. the next top activity is on a different display), there won't have activity
// state change to update it.
- mAtmService.setResumedActivityUncheckLocked(rootTask.getResumedActivity(), reason);
+ mAtmService.setResumedActivityUncheckLocked(rootTask.getTopResumedActivity(), reason);
}
return rootTask;
}
@@ -3563,18 +2820,6 @@
mForceShowForAllUsers = forceShowForAllUsers;
}
- @Override
- public boolean isAttached() {
- final TaskDisplayArea taskDisplayArea = getDisplayArea();
- return taskDisplayArea != null && !taskDisplayArea.isRemoved();
- }
-
- @Override
- @Nullable
- TaskDisplayArea getDisplayArea() {
- return (TaskDisplayArea) super.getDisplayArea();
- }
-
/**
* When we are in a floating root task (Freeform, Pinned, ...) we calculate
* insets differently. However if we are animating to the fullscreen root task
@@ -3585,45 +2830,6 @@
return getWindowConfiguration().tasksAreFloating() && !mPreserveNonFloatingState;
}
- /**
- * Returns true if the root task is translucent and can have other contents visible behind it if
- * needed. A root task is considered translucent if it don't contain a visible or
- * starting (about to be visible) activity that is fullscreen (opaque).
- * @param starting The currently starting activity or null if there is none.
- */
- @VisibleForTesting
- boolean isTranslucent(ActivityRecord starting) {
- if (!isAttached() || isForceHidden()) {
- return true;
- }
- final PooledPredicate p = PooledLambda.obtainPredicate(Task::isOpaqueActivity,
- PooledLambda.__(ActivityRecord.class), starting);
- final ActivityRecord opaque = getActivity(p);
- p.recycle();
- return opaque == null;
- }
-
- private static boolean isOpaqueActivity(ActivityRecord r, ActivityRecord starting) {
- if (r.finishing) {
- // We don't factor in finishing activities when determining translucency since
- // they will be gone soon.
- return false;
- }
-
- if (!r.visibleIgnoringKeyguard && r != starting) {
- // Also ignore invisible activities that are not the currently starting
- // activity (about to be visible).
- return false;
- }
-
- if (r.occludesParent()) {
- // Root task isn't translucent if it has at least one fullscreen activity
- // that is visible.
- return true;
- }
- return false;
- }
-
/** Returns the top-most activity that occludes the given one, or {@code null} if none. */
@Nullable
ActivityRecord getOccludingActivityAbove(ActivityRecord activity) {
@@ -3717,19 +2923,6 @@
return activity != null ? activity.findMainWindow() : null;
}
- ActivityRecord topRunningActivity() {
- return topRunningActivity(false /* focusableOnly */);
- }
-
- ActivityRecord topRunningActivity(boolean focusableOnly) {
- // Split into 2 to avoid object creation due to variable capture.
- if (focusableOnly) {
- return getActivity((r) -> r.canBeTopRunning() && r.isFocusable());
- } else {
- return getActivity(ActivityRecord::canBeTopRunning);
- }
- }
-
ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) {
final PooledPredicate p = PooledLambda.obtainPredicate(Task::isTopRunningNonDelayed
, PooledLambda.__(ActivityRecord.class), notTop);
@@ -3784,12 +2977,6 @@
});
}
- boolean isTopActivityFocusable() {
- final ActivityRecord r = topRunningActivity();
- return r != null ? r.isFocusable()
- : (isFocusable() && getWindowConfiguration().canReceiveKeys());
- }
-
boolean isFocusableAndVisible() {
return isTopActivityFocusable() && shouldBeVisible(null /* starting */);
}
@@ -4136,6 +3323,7 @@
: INVALID_TASK_ID;
info.isFocused = isFocused();
info.isVisible = hasVisibleChildren();
+ info.isSleeping = shouldSleepActivities();
ActivityRecord topRecord = getTopNonFinishingActivity();
info.mTopActivityLocusId = topRecord != null ? topRecord.getLocusId() : null;
}
@@ -4203,184 +3391,6 @@
return this;
}
- /**
- * Returns true if the task should be visible.
- *
- * @param starting The currently starting activity or null if there is none.
- */
- boolean shouldBeVisible(ActivityRecord starting) {
- return getVisibility(starting) != TASK_VISIBILITY_INVISIBLE;
- }
-
- /**
- * Returns true if the task should be visible.
- *
- * @param starting The currently starting activity or null if there is none.
- */
- @TaskVisibility
- int getVisibility(ActivityRecord starting) {
- if (!isAttached() || isForceHidden()) {
- return TASK_VISIBILITY_INVISIBLE;
- }
-
- if (isTopActivityLaunchedBehind()) {
- return TASK_VISIBILITY_VISIBLE;
- }
-
- boolean gotRootSplitScreenTask = false;
- boolean gotOpaqueSplitScreenPrimary = false;
- boolean gotOpaqueSplitScreenSecondary = false;
- boolean gotTranslucentFullscreen = false;
- boolean gotTranslucentSplitScreenPrimary = false;
- boolean gotTranslucentSplitScreenSecondary = false;
- boolean shouldBeVisible = true;
-
- // This root task is only considered visible if all its parent root tasks are considered
- // visible, so check the visibility of all ancestor root task first.
- final WindowContainer parent = getParent();
- if (parent.asTask() != null) {
- final int parentVisibility = parent.asTask().getVisibility(starting);
- if (parentVisibility == TASK_VISIBILITY_INVISIBLE) {
- // Can't be visible if parent isn't visible
- return TASK_VISIBILITY_INVISIBLE;
- } else if (parentVisibility == TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT) {
- // Parent is behind a translucent container so the highest visibility this container
- // can get is that.
- gotTranslucentFullscreen = true;
- }
- }
-
- final List<Task> adjacentTasks = new ArrayList<>();
- final int windowingMode = getWindowingMode();
- final boolean isAssistantType = isActivityTypeAssistant();
- for (int i = parent.getChildCount() - 1; i >= 0; --i) {
- final WindowContainer wc = parent.getChildAt(i);
- final Task other = wc.asTask();
- if (other == null) continue;
-
- final boolean hasRunningActivities = other.topRunningActivity() != null;
- if (other == this) {
- // Should be visible if there is no other stack occluding it, unless it doesn't
- // have any running activities, not starting one and not home stack.
- shouldBeVisible = hasRunningActivities || isInTask(starting) != null
- || isActivityTypeHome();
- break;
- }
-
- if (!hasRunningActivities) {
- continue;
- }
-
- final int otherWindowingMode = other.getWindowingMode();
-
- if (otherWindowingMode == WINDOWING_MODE_FULLSCREEN) {
- if (other.isTranslucent(starting)) {
- // Can be visible behind a translucent fullscreen stack.
- gotTranslucentFullscreen = true;
- continue;
- }
- return TASK_VISIBILITY_INVISIBLE;
- } else if (otherWindowingMode == WINDOWING_MODE_MULTI_WINDOW
- && other.matchParentBounds()) {
- if (other.isTranslucent(starting)) {
- // Can be visible behind a translucent task.
- gotTranslucentFullscreen = true;
- continue;
- }
- // Multi-window task that matches parent bounds would occlude other children.
- return TASK_VISIBILITY_INVISIBLE;
- } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- && !gotOpaqueSplitScreenPrimary) {
- gotRootSplitScreenTask = true;
- gotTranslucentSplitScreenPrimary = other.isTranslucent(starting);
- gotOpaqueSplitScreenPrimary = !gotTranslucentSplitScreenPrimary;
- if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- && gotOpaqueSplitScreenPrimary) {
- // Can not be visible behind another opaque stack in split-screen-primary mode.
- return TASK_VISIBILITY_INVISIBLE;
- }
- } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
- && !gotOpaqueSplitScreenSecondary) {
- gotRootSplitScreenTask = true;
- gotTranslucentSplitScreenSecondary = other.isTranslucent(starting);
- gotOpaqueSplitScreenSecondary = !gotTranslucentSplitScreenSecondary;
- if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
- && gotOpaqueSplitScreenSecondary) {
- // Can not be visible behind another opaque stack in split-screen-secondary mode.
- return TASK_VISIBILITY_INVISIBLE;
- }
- }
- if (gotOpaqueSplitScreenPrimary && gotOpaqueSplitScreenSecondary) {
- // Can not be visible if we are in split-screen windowing mode and both halves of
- // the screen are opaque.
- return TASK_VISIBILITY_INVISIBLE;
- }
- if (isAssistantType && gotRootSplitScreenTask) {
- // Assistant stack can't be visible behind split-screen. In addition to this not
- // making sense, it also works around an issue here we boost the z-order of the
- // assistant window surfaces in window manager whenever it is visible.
- return TASK_VISIBILITY_INVISIBLE;
- }
- if (other.mAdjacentTask != null) {
- if (adjacentTasks.contains(other.mAdjacentTask)) {
- if (other.isTranslucent(starting)
- || other.mAdjacentTask.isTranslucent(starting)) {
- // Can be visible behind a translucent adjacent tasks.
- gotTranslucentFullscreen = true;
- continue;
- }
- // Can not be visible behind adjacent tasks.
- return TASK_VISIBILITY_INVISIBLE;
- } else {
- adjacentTasks.add(other);
- }
- }
- }
-
- if (!shouldBeVisible) {
- return TASK_VISIBILITY_INVISIBLE;
- }
-
- // Handle cases when there can be a translucent split-screen stack on top.
- switch (windowingMode) {
- case WINDOWING_MODE_FULLSCREEN:
- if (gotTranslucentSplitScreenPrimary || gotTranslucentSplitScreenSecondary) {
- // At least one of the split-screen stacks that covers this one is translucent.
- // When in split mode, home task will be reparented to the secondary split while
- // leaving tasks not supporting split below. Due to
- // TaskDisplayArea#assignRootTaskOrdering always adjusts home surface layer to
- // the bottom, this makes sure tasks not in split roots won't occlude home task
- // unexpectedly.
- return TASK_VISIBILITY_INVISIBLE;
- }
- break;
- case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
- if (gotTranslucentSplitScreenPrimary) {
- // Covered by translucent primary split-screen on top.
- return TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
- }
- break;
- case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY:
- if (gotTranslucentSplitScreenSecondary) {
- // Covered by translucent secondary split-screen on top.
- return TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
- }
- break;
- }
-
- // Lastly - check if there is a translucent fullscreen stack on top.
- return gotTranslucentFullscreen ? TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT
- : TASK_VISIBILITY_VISIBLE;
- }
-
- private boolean isTopActivityLaunchedBehind() {
- final ActivityRecord top = topRunningActivity();
- if (top != null && top.mLaunchTaskBehind) {
- return true;
- }
- return false;
- }
-
ActivityRecord isInTask(ActivityRecord r) {
if (r == null) {
return null;
@@ -4563,7 +3573,7 @@
// Increment the total number of non-finishing activities
numActivities++;
- if (top == null || (top.isState(ActivityState.INITIALIZING))) {
+ if (top == null || (top.isState(INITIALIZING))) {
top = r;
// Reset the number of running activities until we hit the first non-initializing
// activity
@@ -5306,9 +4316,7 @@
return super.isAlwaysOnTop();
}
- /**
- * Returns whether this task is currently forced to be hidden for any reason.
- */
+ @Override
protected boolean isForceHidden() {
return mForceHiddenFlags != 0;
}
@@ -5595,19 +4603,6 @@
r.completeResumeLocked();
}
- void awakeFromSleepingLocked() {
- if (!isLeafTask()) {
- forAllLeafTasks((task) -> task.awakeFromSleepingLocked(),
- true /* traverseTopToBottom */);
- return;
- }
-
- if (mPausingActivity != null) {
- Slog.d(TAG, "awakeFromSleepingLocked: previously pausing activity didn't pause");
- mPausingActivity.activityPaused(true);
- }
- }
-
void checkReadyForSleep() {
if (shouldSleepActivities() && goToSleepIfPossible(false /* shuttingDown */)) {
mTaskSupervisor.checkReadyForSleepLocked(true /* allowDelay */);
@@ -5626,302 +4621,13 @@
* the process of going to sleep (checkReadyForSleep will be called when that process finishes).
*/
boolean goToSleepIfPossible(boolean shuttingDown) {
- if (!isLeafTask()) {
- final int[] sleepInProgress = {0};
- forAllLeafTasks((t) -> {
- if (!t.goToSleepIfPossible(shuttingDown)) {
- sleepInProgress[0]++;
- }
- }, true);
- return sleepInProgress[0] == 0;
- }
-
- boolean shouldSleep = true;
- if (mResumedActivity != null) {
- // Still have something resumed; can't sleep until it is paused.
- ProtoLog.v(WM_DEBUG_STATES, "Sleep needs to pause %s", mResumedActivity);
- if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
- "Sleep => pause with userLeaving=false");
-
- startPausingLocked(false /* userLeaving */, true /* uiSleeping */, null /* resuming */,
- "sleep");
- shouldSleep = false ;
- } else if (mPausingActivity != null) {
- // Still waiting for something to pause; can't sleep yet.
- ProtoLog.v(WM_DEBUG_STATES, "Sleep still waiting to pause %s", mPausingActivity);
- shouldSleep = false;
- }
-
- if (!shuttingDown) {
- if (containsActivityFromRootTask(mTaskSupervisor.mStoppingActivities)) {
- // Still need to tell some activities to stop; can't sleep yet.
- ProtoLog.v(WM_DEBUG_STATES, "Sleep still need to stop %d activities",
- mTaskSupervisor.mStoppingActivities.size());
-
- mTaskSupervisor.scheduleIdle();
- shouldSleep = false;
+ final int[] sleepInProgress = {0};
+ forAllLeafTaskFragments((f) -> {
+ if (!f.sleepIfPossible(shuttingDown)) {
+ sleepInProgress[0]++;
}
- }
-
- if (shouldSleep) {
- ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
- !PRESERVE_WINDOWS);
- }
-
- return shouldSleep;
- }
-
- private boolean containsActivityFromRootTask(List<ActivityRecord> rs) {
- for (ActivityRecord r : rs) {
- if (r.getRootTask() == this) {
- return true;
- }
- }
- return false;
- }
-
- final boolean startPausingLocked(boolean uiSleeping, ActivityRecord resuming, String reason) {
- return startPausingLocked(mTaskSupervisor.mUserLeaving, uiSleeping, resuming, reason);
- }
-
- /**
- * Start pausing the currently resumed activity. It is an error to call this if there
- * is already an activity being paused or there is no resumed activity.
- *
- * @param userLeaving True if this should result in an onUserLeaving to the current activity.
- * @param uiSleeping True if this is happening with the user interface going to sleep (the
- * screen turning off).
- * @param resuming The activity we are currently trying to resume or null if this is not being
- * called as part of resuming the top activity, so we shouldn't try to instigate
- * a resume here if not null.
- * @param reason The reason of pausing the activity.
- * @return Returns true if an activity now is in the PAUSING state, and we are waiting for
- * it to tell us when it is done.
- */
- final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping,
- ActivityRecord resuming, String reason) {
- if (!isLeafTask()) {
- final int[] pausing = {0};
- forAllLeafTasks((t) -> {
- if (t.startPausingLocked(userLeaving, uiSleeping, resuming, reason)) {
- pausing[0]++;
- }
- }, true /* traverseTopToBottom */);
- return pausing[0] > 0;
- }
-
- if (mPausingActivity != null) {
- Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity
- + " state=" + mPausingActivity.getState());
- if (!shouldSleepActivities()) {
- // Avoid recursion among check for sleep and complete pause during sleeping.
- // Because activity will be paused immediately after resume, just let pause
- // be completed by the order of activity paused from clients.
- completePauseLocked(false, resuming);
- }
- }
- ActivityRecord prev = mResumedActivity;
-
- if (prev == null) {
- if (resuming == null) {
- Slog.wtf(TAG, "Trying to pause when nothing is resumed");
- mRootWindowContainer.resumeFocusedTasksTopActivities();
- }
- return false;
- }
-
- if (prev == resuming) {
- Slog.wtf(TAG, "Trying to pause activity that is in process of being resumed");
- return false;
- }
-
- ProtoLog.v(WM_DEBUG_STATES, "Moving to PAUSING: %s", prev);
- mPausingActivity = prev;
- mLastPausedActivity = prev;
- if (prev.isNoHistory() && !mTaskSupervisor.mNoHistoryActivities.contains(prev)) {
- mTaskSupervisor.mNoHistoryActivities.add(prev);
- }
- prev.setState(PAUSING, "startPausingLocked");
- prev.getTask().touchActiveTime();
-
- mAtmService.updateCpuStats();
-
- boolean pauseImmediately = false;
- boolean shouldAutoPip = false;
- if (resuming != null && (resuming.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0) {
- // If the flag RESUME_WHILE_PAUSING is set, then continue to schedule the previous
- // activity to be paused, while at the same time resuming the new resume activity
- // only if the previous activity can't go into Pip since we want to give Pip
- // activities a chance to enter Pip before resuming the next activity.
- final boolean lastResumedCanPip = prev != null && prev.checkEnterPictureInPictureState(
- "shouldResumeWhilePausing", userLeaving);
- if (lastResumedCanPip && prev.pictureInPictureArgs.isAutoEnterEnabled()) {
- shouldAutoPip = true;
- } else if (!lastResumedCanPip) {
- pauseImmediately = true;
- } else {
- // The previous activity may still enter PIP even though it did not allow auto-PIP.
- }
- }
-
- boolean didAutoPip = false;
- if (prev.attachedToProcess()) {
- if (shouldAutoPip) {
- ProtoLog.d(WM_DEBUG_STATES, "Auto-PIP allowed, entering PIP mode "
- + "directly: %s", prev);
-
- didAutoPip = mAtmService.enterPictureInPictureMode(prev, prev.pictureInPictureArgs);
- mPausingActivity = null;
- } else {
- ProtoLog.v(WM_DEBUG_STATES, "Enqueueing pending pause: %s", prev);
- try {
- EventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev),
- prev.shortComponentName, "userLeaving=" + userLeaving, reason);
-
- mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
- prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
- prev.configChangeFlags, pauseImmediately));
- } catch (Exception e) {
- // Ignore exception, if process died other code will cleanup.
- Slog.w(TAG, "Exception thrown during pause", e);
- mPausingActivity = null;
- mLastPausedActivity = null;
- mTaskSupervisor.mNoHistoryActivities.remove(prev);
- }
- }
- } else {
- mPausingActivity = null;
- mLastPausedActivity = null;
- mTaskSupervisor.mNoHistoryActivities.remove(prev);
- }
-
- // If we are not going to sleep, we want to ensure the device is
- // awake until the next activity is started.
- if (!uiSleeping && !mAtmService.isSleepingOrShuttingDownLocked()) {
- mTaskSupervisor.acquireLaunchWakelock();
- }
-
- // If already entered PIP mode, no need to keep pausing.
- if (mPausingActivity != null && !didAutoPip) {
- // Have the window manager pause its key dispatching until the new
- // activity has started. If we're pausing the activity just because
- // the screen is being turned off and the UI is sleeping, don't interrupt
- // key dispatch; the same activity will pick it up again on wakeup.
- if (!uiSleeping) {
- prev.pauseKeyDispatchingLocked();
- } else {
- ProtoLog.v(WM_DEBUG_STATES, "Key dispatch not paused for screen off");
- }
-
- if (pauseImmediately) {
- // If the caller said they don't want to wait for the pause, then complete
- // the pause now.
- completePauseLocked(false, resuming);
- return false;
-
- } else {
- prev.schedulePauseTimeout();
- return true;
- }
-
- } else {
- // This activity either failed to schedule the pause or it entered PIP mode,
- // so just treat it as being paused now.
- ProtoLog.v(WM_DEBUG_STATES, "Activity not running or entered PiP, resuming next.");
- if (resuming == null) {
- mRootWindowContainer.resumeFocusedTasksTopActivities();
- }
- return false;
- }
- }
-
- @VisibleForTesting
- void completePauseLocked(boolean resumeNext, ActivityRecord resuming) {
- // Complete the pausing process of a pausing activity, so it doesn't make sense to
- // operate on non-leaf tasks.
- warnForNonLeafTask("completePauseLocked");
-
- ActivityRecord prev = mPausingActivity;
- ProtoLog.v(WM_DEBUG_STATES, "Complete pause: %s", prev);
-
- if (prev != null) {
- prev.setWillCloseOrEnterPip(false);
- final boolean wasStopping = prev.isState(STOPPING);
- prev.setState(PAUSED, "completePausedLocked");
- if (prev.finishing) {
- // We will update the activity visibility later, no need to do in
- // completeFinishing(). Updating visibility here might also making the next
- // activities to be resumed, and could result in wrong app transition due to
- // lack of previous activity information.
- ProtoLog.v(WM_DEBUG_STATES, "Executing finish of activity: %s", prev);
- prev = prev.completeFinishing(false /* updateVisibility */,
- "completePausedLocked");
- } else if (prev.hasProcess()) {
- ProtoLog.v(WM_DEBUG_STATES, "Enqueue pending stop if needed: %s "
- + "wasStopping=%b visibleRequested=%b", prev, wasStopping,
- prev.mVisibleRequested);
- if (prev.deferRelaunchUntilPaused) {
- // Complete the deferred relaunch that was waiting for pause to complete.
- ProtoLog.v(WM_DEBUG_STATES, "Re-launching after pause: %s", prev);
- prev.relaunchActivityLocked(prev.preserveWindowOnDeferredRelaunch);
- } else if (wasStopping) {
- // We are also stopping, the stop request must have gone soon after the pause.
- // We can't clobber it, because the stop confirmation will not be handled.
- // We don't need to schedule another stop, we only need to let it happen.
- prev.setState(STOPPING, "completePausedLocked");
- } else if (!prev.mVisibleRequested || shouldSleepOrShutDownActivities()) {
- // Clear out any deferred client hide we might currently have.
- prev.setDeferHidingClient(false);
- // If we were visible then resumeTopActivities will release resources before
- // stopping.
- prev.addToStopping(true /* scheduleIdle */, false /* idleDelayed */,
- "completePauseLocked");
- }
- } else {
- ProtoLog.v(WM_DEBUG_STATES, "App died during pause, not stopping: %s", prev);
- prev = null;
- }
- // It is possible the activity was freezing the screen before it was paused.
- // In that case go ahead and remove the freeze this activity has on the screen
- // since it is no longer visible.
- if (prev != null) {
- prev.stopFreezingScreenLocked(true /*force*/);
- }
- mPausingActivity = null;
- }
-
- if (resumeNext) {
- final Task topRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask();
- if (topRootTask != null && !topRootTask.shouldSleepOrShutDownActivities()) {
- mRootWindowContainer.resumeFocusedTasksTopActivities(topRootTask, prev, null);
- } else {
- checkReadyForSleep();
- final ActivityRecord top =
- topRootTask != null ? topRootTask.topRunningActivity() : null;
- if (top == null || (prev != null && top != prev)) {
- // If there are no more activities available to run, do resume anyway to start
- // something. Also if the top activity on the root task is not the just paused
- // activity, we need to go ahead and resume it to ensure we complete an
- // in-flight app switch.
- mRootWindowContainer.resumeFocusedTasksTopActivities();
- }
- }
- }
-
- if (prev != null) {
- prev.resumeKeyDispatchingLocked();
- }
-
- mRootWindowContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS);
-
- // Notify when the task stack has changed, but only if visibilities changed (not just
- // focus). Also if there is an active root pinned task - we always want to notify it about
- // task stack changes, because its positioning may depend on it.
- if (mTaskSupervisor.mAppVisibilitiesChangedSinceLastPause
- || (getDisplayArea() != null && getDisplayArea().hasPinnedTask())) {
- mAtmService.getTaskChangeNotificationController().notifyTaskStackChanged();
- mTaskSupervisor.mAppVisibilitiesChangedSinceLastPause = false;
- }
+ }, true);
+ return sleepInProgress[0] == 0;
}
boolean isTopRootTaskInDisplayArea() {
@@ -5944,10 +4650,10 @@
* The activity is either starting or resuming.
* Caller should ensure starting activity is visible.
* @param preserveWindows Flag indicating whether windows should be preserved when updating
- * configuration in {@link mEnsureActivitiesVisibleHelper}.
+ * configuration in {@link EnsureActivitiesVisibleHelper}.
* @param configChanges Parts of the configuration that changed for this activity for evaluating
* if the screen should be frozen as part of
- * {@link mEnsureActivitiesVisibleHelper}.
+ * {@link EnsureActivitiesVisibleHelper}.
*
*/
void ensureActivitiesVisible(@Nullable ActivityRecord starting, int configChanges,
@@ -5963,21 +4669,22 @@
* The activity is either starting or resuming.
* Caller should ensure starting activity is visible.
* @param notifyClients Flag indicating whether the visibility updates should be sent to the
- * clients in {@link mEnsureActivitiesVisibleHelper}.
+ * clients in {@link EnsureActivitiesVisibleHelper}.
* @param preserveWindows Flag indicating whether windows should be preserved when updating
- * configuration in {@link mEnsureActivitiesVisibleHelper}.
+ * configuration in {@link EnsureActivitiesVisibleHelper}.
* @param configChanges Parts of the configuration that changed for this activity for evaluating
* if the screen should be frozen as part of
- * {@link mEnsureActivitiesVisibleHelper}.
+ * {@link EnsureActivitiesVisibleHelper}.
*/
// TODO: Should be re-worked based on the fact that each task as a root task in most cases.
void ensureActivitiesVisible(@Nullable ActivityRecord starting, int configChanges,
boolean preserveWindows, boolean notifyClients) {
mTaskSupervisor.beginActivityVisibilityUpdate();
try {
- forAllLeafTasks(task -> task.mEnsureActivitiesVisibleHelper.process(
- starting, configChanges, preserveWindows, notifyClients),
- true /* traverseTopToBottom */);
+ forAllLeafTaskFragments(fragment -> {
+ fragment.updateActivityVisibilities(starting, configChanges, preserveWindows,
+ notifyClients);
+ }, true /* traverseTopToBottom */);
// Notify WM shell that task visibilities may have changed
forAllTasks(task -> task.dispatchTaskInfoChangedIfNeeded(/* force */ false),
@@ -6052,25 +4759,6 @@
}
}
- /** @see ActivityRecord#cancelInitializing() */
- void cancelInitializingActivities() {
- // We don't want to clear starting window for activities that aren't behind fullscreen
- // activities as we need to display their starting window until they are done initializing.
- checkBehindFullscreenActivity(null /* toCheck */, ActivityRecord::cancelInitializing);
- }
-
- /**
- * If an activity {@param toCheck} is given, this method returns {@code true} if the activity
- * is occluded by any fullscreen activity. If there is no {@param toCheck} and the handling
- * function {@param handleBehindFullscreenActivity} is given, this method will pass all occluded
- * activities to the function.
- */
- boolean checkBehindFullscreenActivity(ActivityRecord toCheck,
- Consumer<ActivityRecord> handleBehindFullscreenActivity) {
- return mCheckBehindFullscreenActivityHelper.process(
- toCheck, handleBehindFullscreenActivity);
- }
-
/**
* Ensure that the top activity in the root task is resumed.
*
@@ -6111,7 +4799,8 @@
if (!child.isTopActivityFocusable()) {
continue;
}
- if (child.getVisibility(null /* starting */) != TASK_VISIBILITY_VISIBLE) {
+ if (child.getVisibility(null /* starting */)
+ != TASK_FRAGMENT_VISIBILITY_VISIBLE) {
break;
}
@@ -6158,383 +4847,18 @@
return false;
}
- // Find the next top-most activity to resume in this root task that is not finishing and is
- // focusable. If it is not focusable, we will fall into the case below to resume the
- // top activity in the next focusable task.
- ActivityRecord next = topRunningActivity(true /* focusableOnly */);
-
- final boolean hasRunningActivity = next != null;
-
- // TODO: Maybe this entire condition can get removed?
- if (hasRunningActivity && !isAttached()) {
- return false;
- }
-
mRootWindowContainer.cancelInitializingActivities();
- if (!hasRunningActivity) {
- // There are no activities left in the root task, let's look somewhere else.
+ if (topRunningActivity(true /* focusableOnly */) == null) {
+ // There are no activities left in this task, let's look somewhere else.
return resumeNextFocusableActivityWhenRootTaskIsEmpty(prev, options);
}
- next.delayedResume = false;
- final TaskDisplayArea taskDisplayArea = getDisplayArea();
-
- // If the top activity is the resumed one, nothing to do.
- if (mResumedActivity == next && next.isState(RESUMED)
- && taskDisplayArea.allResumedActivitiesComplete()) {
- // Make sure we have executed any pending transitions, since there
- // should be nothing left to do at this point.
- executeAppTransition(options);
- // For devices that are not in fullscreen mode (e.g. freeform windows), it's possible
- // we still want to check if the visibility of other windows have changed (e.g. bringing
- // a fullscreen window forward to cover another freeform activity.)
- if (taskDisplayArea.inMultiWindowMode()) {
- taskDisplayArea.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
- false /* preserveWindows */, true /* notifyClients */);
- }
- ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Top activity "
- + "resumed %s", next);
- return false;
- }
-
- if (!next.canResumeByCompat()) {
- return false;
- }
-
- // If we are currently pausing an activity, then don't do anything until that is done.
- final boolean allPausedComplete = mRootWindowContainer.allPausedActivitiesComplete();
- if (!allPausedComplete) {
- ProtoLog.v(WM_DEBUG_STATES,
- "resumeTopActivityLocked: Skip resume: some activity pausing.");
-
- return false;
- }
-
- // If we are sleeping, and there is no resumed activity, and the top activity is paused,
- // well that is the state we want.
- if (mLastPausedActivity == next && shouldSleepOrShutDownActivities()) {
- // Make sure we have executed any pending transitions, since there
- // should be nothing left to do at this point.
- executeAppTransition(options);
- ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Going to sleep and"
- + " all paused");
- return false;
- }
-
- // Make sure that the user who owns this activity is started. If not,
- // we will just leave it as is because someone should be bringing
- // another user's activities to the top of the stack.
- if (!mAtmService.mAmInternal.hasStartedUserState(next.mUserId)) {
- Slog.w(TAG, "Skipping resume of top activity " + next
- + ": user " + next.mUserId + " is stopped");
- return false;
- }
-
- // The activity may be waiting for stop, but that is no longer
- // appropriate for it.
- mTaskSupervisor.mStoppingActivities.remove(next);
-
- if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next);
-
- mTaskSupervisor.setLaunchSource(next.info.applicationInfo.uid);
-
- ActivityRecord lastResumed = null;
- final Task lastFocusedRootTask = taskDisplayArea.getLastFocusedRootTask();
- if (lastFocusedRootTask != null && lastFocusedRootTask != getRootTask()) {
- // So, why aren't we using prev here??? See the param comment on the method. prev
- // doesn't represent the last resumed activity. However, the last focus stack does if
- // it isn't null.
- lastResumed = lastFocusedRootTask.getResumedActivity();
- }
-
- boolean pausing = !deferPause && taskDisplayArea.pauseBackTasks(next);
- if (mResumedActivity != null) {
- ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Pausing %s", mResumedActivity);
- pausing |= startPausingLocked(false /* uiSleeping */, next,
- "resumeTopActivityInnerLocked");
- }
- if (pausing) {
- ProtoLog.v(WM_DEBUG_STATES, "resumeTopActivityLocked: Skip resume: need to"
- + " start pausing");
- // At this point we want to put the upcoming activity's process
- // at the top of the LRU list, since we know we will be needing it
- // very soon and it would be a waste to let it get killed if it
- // happens to be sitting towards the end.
- if (next.attachedToProcess()) {
- next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,
- true /* activityChange */, false /* updateOomAdj */,
- false /* addPendingTopUid */);
- } else if (!next.isProcessRunning()) {
- // Since the start-process is asynchronous, if we already know the process of next
- // activity isn't running, we can start the process earlier to save the time to wait
- // for the current activity to be paused.
- final boolean isTop = this == taskDisplayArea.getFocusedRootTask();
- mAtmService.startProcessAsync(next, false /* knownToBeDead */, isTop,
- isTop ? "pre-top-activity" : "pre-activity");
- }
- if (lastResumed != null) {
- lastResumed.setWillCloseOrEnterPip(true);
- }
- return true;
- } else if (mResumedActivity == next && next.isState(RESUMED)
- && taskDisplayArea.allResumedActivitiesComplete()) {
- // It is possible for the activity to be resumed when we paused back stacks above if the
- // next activity doesn't have to wait for pause to complete.
- // So, nothing else to-do except:
- // Make sure we have executed any pending transitions, since there
- // should be nothing left to do at this point.
- executeAppTransition(options);
- ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Top activity resumed "
- + "(dontWaitForPause) %s", next);
- return true;
- }
-
- // If the most recent activity was noHistory but was only stopped rather
- // than stopped+finished because the device went to sleep, we need to make
- // sure to finish it as we're making a new activity topmost.
- if (shouldSleepActivities()) {
- mTaskSupervisor.finishNoHistoryActivitiesIfNeeded(next);
- }
-
- if (prev != null && prev != next && next.nowVisible) {
-
- // The next activity is already visible, so hide the previous
- // activity's windows right now so we can show the new one ASAP.
- // We only do this if the previous is finishing, which should mean
- // it is on top of the one being resumed so hiding it quickly
- // is good. Otherwise, we want to do the normal route of allowing
- // the resumed activity to be shown so we can decide if the
- // previous should actually be hidden depending on whether the
- // new one is found to be full-screen or not.
- if (prev.finishing) {
- prev.setVisibility(false);
- if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
- "Not waiting for visible to hide: " + prev
- + ", nowVisible=" + next.nowVisible);
- } else {
- if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
- "Previous already visible but still waiting to hide: " + prev
- + ", nowVisible=" + next.nowVisible);
- }
-
- }
-
- // Launching this app's activity, make sure the app is no longer
- // considered stopped.
- try {
- mTaskSupervisor.getActivityMetricsLogger()
- .notifyBeforePackageUnstopped(next.packageName);
- mAtmService.getPackageManager().setPackageStoppedState(
- next.packageName, false, next.mUserId); /* TODO: Verify if correct userid */
- } catch (RemoteException e1) {
- } catch (IllegalArgumentException e) {
- Slog.w(TAG, "Failed trying to unstop package "
- + next.packageName + ": " + e);
- }
-
- // We are starting up the next activity, so tell the window manager
- // that the previous one will be hidden soon. This way it can know
- // to ignore it when computing the desired screen orientation.
- boolean anim = true;
- final DisplayContent dc = taskDisplayArea.mDisplayContent;
- if (prev != null) {
- if (prev.finishing) {
- if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
- "Prepare close transition: prev=" + prev);
- if (mTaskSupervisor.mNoAnimActivities.contains(prev)) {
- anim = false;
- dc.prepareAppTransition(TRANSIT_NONE);
- } else {
- dc.prepareAppTransition(TRANSIT_CLOSE);
- }
- prev.setVisibility(false);
- } else {
- if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
- "Prepare open transition: prev=" + prev);
- if (mTaskSupervisor.mNoAnimActivities.contains(next)) {
- anim = false;
- dc.prepareAppTransition(TRANSIT_NONE);
- } else {
- dc.prepareAppTransition(TRANSIT_OPEN,
- next.mLaunchTaskBehind ? TRANSIT_FLAG_OPEN_BEHIND : 0);
- }
- }
- } else {
- if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: no previous");
- if (mTaskSupervisor.mNoAnimActivities.contains(next)) {
- anim = false;
- dc.prepareAppTransition(TRANSIT_NONE);
- } else {
- dc.prepareAppTransition(TRANSIT_OPEN);
- }
- }
-
- if (anim) {
- next.applyOptionsAnimation();
- } else {
- next.abortAndClearOptionsAnimation();
- }
-
- mTaskSupervisor.mNoAnimActivities.clear();
-
- if (next.attachedToProcess()) {
- if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resume running: " + next
- + " stopped=" + next.stopped
- + " visibleRequested=" + next.mVisibleRequested);
-
- // If the previous activity is translucent, force a visibility update of
- // the next activity, so that it's added to WM's opening app list, and
- // transition animation can be set up properly.
- // For example, pressing Home button with a translucent activity in focus.
- // Launcher is already visible in this case. If we don't add it to opening
- // apps, maybeUpdateTransitToWallpaper() will fail to identify this as a
- // TRANSIT_WALLPAPER_OPEN animation, and run some funny animation.
- final boolean lastActivityTranslucent = lastFocusedRootTask != null
- && (lastFocusedRootTask.inMultiWindowMode()
- || (lastFocusedRootTask.mLastPausedActivity != null
- && !lastFocusedRootTask.mLastPausedActivity.occludesParent()));
-
- // This activity is now becoming visible.
- if (!next.mVisibleRequested || next.stopped || lastActivityTranslucent) {
- next.setVisibility(true);
- }
-
- // schedule launch ticks to collect information about slow apps.
- next.startLaunchTickingLocked();
-
- ActivityRecord lastResumedActivity =
- lastFocusedRootTask == null ? null : lastFocusedRootTask.getResumedActivity();
- final ActivityState lastState = next.getState();
-
- mAtmService.updateCpuStats();
-
- ProtoLog.v(WM_DEBUG_STATES, "Moving to RESUMED: %s (in existing)", next);
-
- next.setState(RESUMED, "resumeTopActivityInnerLocked");
-
- // Have the window manager re-evaluate the orientation of
- // the screen based on the new activity order.
- boolean notUpdated = true;
-
- // Activity should also be visible if set mLaunchTaskBehind to true (see
- // ActivityRecord#shouldBeVisibleIgnoringKeyguard()).
- if (shouldBeVisible(next)) {
- // We have special rotation behavior when here is some active activity that
- // requests specific orientation or Keyguard is locked. Make sure all activity
- // visibilities are set correctly as well as the transition is updated if needed
- // to get the correct rotation behavior. Otherwise the following call to update
- // the orientation may cause incorrect configurations delivered to client as a
- // result of invisible window resize.
- // TODO: Remove this once visibilities are set correctly immediately when
- // starting an activity.
- notUpdated = !mRootWindowContainer.ensureVisibilityAndConfig(next, getDisplayId(),
- true /* markFrozenIfConfigChanged */, false /* deferResume */);
- }
-
- if (notUpdated) {
- // The configuration update wasn't able to keep the existing
- // instance of the activity, and instead started a new one.
- // We should be all done, but let's just make sure our activity
- // is still at the top and schedule another run if something
- // weird happened.
- ActivityRecord nextNext = topRunningActivity();
- ProtoLog.i(WM_DEBUG_STATES, "Activity config changed during resume: "
- + "%s, new next: %s", next, nextNext);
- if (nextNext != next) {
- // Do over!
- mTaskSupervisor.scheduleResumeTopActivities();
- }
- if (!next.mVisibleRequested || next.stopped) {
- next.setVisibility(true);
- }
- next.completeResumeLocked();
- return true;
- }
-
- try {
- final ClientTransaction transaction =
- ClientTransaction.obtain(next.app.getThread(), next.appToken);
- // Deliver all pending results.
- ArrayList<ResultInfo> a = next.results;
- if (a != null) {
- final int N = a.size();
- if (!next.finishing && N > 0) {
- if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
- "Delivering results to " + next + ": " + a);
- transaction.addCallback(ActivityResultItem.obtain(a));
- }
- }
-
- if (next.newIntents != null) {
- transaction.addCallback(
- NewIntentItem.obtain(next.newIntents, true /* resume */));
- }
-
- // Well the app will no longer be stopped.
- // Clear app token stopped state in window manager if needed.
- next.notifyAppResumed(next.stopped);
-
- EventLogTags.writeWmResumeActivity(next.mUserId, System.identityHashCode(next),
- next.getTask().mTaskId, next.shortComponentName);
-
- mAtmService.getAppWarningsLocked().onResumeActivity(next);
- next.app.setPendingUiCleanAndForceProcessStateUpTo(mAtmService.mTopProcessState);
- next.abortAndClearOptionsAnimation();
- transaction.setLifecycleStateRequest(
- ResumeActivityItem.obtain(next.app.getReportedProcState(),
- dc.isNextTransitionForward()));
- mAtmService.getLifecycleManager().scheduleTransaction(transaction);
-
- ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Resumed %s", next);
- } catch (Exception e) {
- // Whoops, need to restart this activity!
- ProtoLog.v(WM_DEBUG_STATES, "Resume failed; resetting state to %s: "
- + "%s", lastState, next);
- next.setState(lastState, "resumeTopActivityInnerLocked");
-
- // lastResumedActivity being non-null implies there is a lastStack present.
- if (lastResumedActivity != null) {
- lastResumedActivity.setState(RESUMED, "resumeTopActivityInnerLocked");
- }
-
- Slog.i(TAG, "Restarting because process died: " + next);
- if (!next.hasBeenLaunched) {
- next.hasBeenLaunched = true;
- } else if (SHOW_APP_STARTING_PREVIEW && lastFocusedRootTask != null
- && lastFocusedRootTask.isTopRootTaskInDisplayArea()) {
- next.showStartingWindow(false /* taskSwitch */);
- }
- mTaskSupervisor.startSpecificActivity(next, true, false);
- return true;
- }
-
- // From this point on, if something goes wrong there is no way
- // to recover the activity.
- try {
- next.completeResumeLocked();
- } catch (Exception e) {
- // If any exception gets thrown, toss away this
- // activity and try the next one.
- Slog.w(TAG, "Exception thrown during resume of " + next, e);
- next.finishIfPossible("resume-exception", true /* oomAdj */);
- return true;
- }
- } else {
- // Whoops, need to restart this activity!
- if (!next.hasBeenLaunched) {
- next.hasBeenLaunched = true;
- } else {
- if (SHOW_APP_STARTING_PREVIEW) {
- next.showStartingWindow(false /* taskSwich */);
- }
- if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Restarting: " + next);
- }
- ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Restarting %s", next);
- mTaskSupervisor.startSpecificActivity(next, true, true);
- }
-
- return true;
+ final boolean[] resumed = new boolean[1];
+ forAllLeafTaskFragments(f -> {
+ resumed[0] |= f.resumeTopActivity(prev, options, deferPause);
+ }, true);
+ return resumed[0];
}
/**
@@ -7213,13 +5537,6 @@
return true;
}
- /**
- * Ensures all visible activities at or below the input activity have the right configuration.
- */
- void ensureVisibleActivitiesConfiguration(ActivityRecord start, boolean preserveWindow) {
- mEnsureVisibleActivitiesConfigHelper.process(start, preserveWindow);
- }
-
// TODO: Can only be called from special methods in ActivityTaskSupervisor.
// Need to consolidate those calls points into this resize method so anyone can call directly.
void resize(Rect displayedBounds, boolean preserveWindows, boolean deferResume) {
@@ -7273,114 +5590,35 @@
}
}
- /**
- * Reset local parameters because an app's activity died.
- * @param app The app of the activity that died.
- * @return {@code true} if the process of the pausing activity is died.
- */
- boolean handleAppDied(WindowProcessController app) {
- warnForNonLeafTask("handleAppDied");
- boolean isPausingDied = false;
- if (mPausingActivity != null && mPausingActivity.app == app) {
- ProtoLog.v(WM_DEBUG_STATES, "App died while pausing: %s",
- mPausingActivity);
- mPausingActivity = null;
- isPausingDied = true;
- }
- if (mLastPausedActivity != null && mLastPausedActivity.app == app) {
- if (mLastPausedActivity.isNoHistory()) {
- mTaskSupervisor.mNoHistoryActivities.remove(mLastPausedActivity);
- }
- mLastPausedActivity = null;
- }
- return isPausingDied;
- }
-
boolean dump(FileDescriptor fd, PrintWriter pw, boolean dumpAll, boolean dumpClient,
String dumpPackage, final boolean needSep) {
- Runnable headerPrinter = () -> {
- if (needSep) {
- pw.println();
- }
- pw.println(" RootTask #" + getRootTaskId()
- + ": type=" + activityTypeToString(getActivityType())
- + " mode=" + windowingModeToString(getWindowingMode()));
- pw.println(" isSleeping=" + shouldSleepActivities());
- pw.println(" mBounds=" + getRequestedOverrideBounds());
- pw.println(" mCreatedByOrganizer=" + mCreatedByOrganizer);
- };
-
- boolean printed = false;
-
- if (dumpPackage == null) {
- // If we are not filtering by package, we want to print absolutely everything,
- // so always print the header even if there are no tasks/activities inside.
- headerPrinter.run();
- headerPrinter = null;
- printed = true;
- }
-
- printed |= printThisActivity(pw, getPausingActivity(), dumpPackage, false,
- " mPausingActivity: ", null);
- printed |= printThisActivity(pw, getResumedActivity(), dumpPackage, false,
- " mResumedActivity: ", null);
- if (dumpAll) {
- printed |= printThisActivity(pw, mLastPausedActivity, dumpPackage, false,
- " mLastPausedActivity: ", null);
- }
-
- printed |= dumpActivities(fd, pw, dumpAll, dumpClient, dumpPackage, false, headerPrinter);
-
- return printed;
+ return dump(" ", fd, pw, dumpAll, dumpClient, dumpPackage, needSep, null /* header */);
}
- private boolean dumpActivities(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
- boolean dumpClient, String dumpPackage, boolean needSep, Runnable header) {
- if (!hasChild()) {
- return false;
+ @Override
+ void dumpInner(String prefix, PrintWriter pw, boolean dumpAll, String dumpPackage) {
+ pw.print(prefix); pw.print("* "); pw.println(this);
+ pw.println(prefix + " mBounds=" + getRequestedOverrideBounds());
+ pw.println(prefix + " mCreatedByOrganizer=" + mCreatedByOrganizer);
+ if (mLastNonFullscreenBounds != null) {
+ pw.print(prefix); pw.print(" mLastNonFullscreenBounds=");
+ pw.println(mLastNonFullscreenBounds);
}
- final AtomicBoolean printedHeader = new AtomicBoolean(false);
- final AtomicBoolean printed = new AtomicBoolean(false);
- forAllLeafTasks((task) -> {
- final String prefix = " ";
- Runnable headerPrinter = () -> {
- printed.set(true);
- if (!printedHeader.get()) {
- if (needSep) {
- pw.println("");
- }
- if (header != null) {
- header.run();
- }
- printedHeader.set(true);
- }
- pw.print(prefix); pw.print("* "); pw.println(task);
- pw.print(prefix); pw.print(" mBounds=");
- pw.println(task.getRequestedOverrideBounds());
- pw.print(prefix); pw.print(" mMinWidth="); pw.print(task.mMinWidth);
- pw.print(" mMinHeight="); pw.println(task.mMinHeight);
- if (mLastNonFullscreenBounds != null) {
- pw.print(prefix);
- pw.print(" mLastNonFullscreenBounds=");
- pw.println(task.mLastNonFullscreenBounds);
- }
- task.dump(pw, prefix + " ");
- };
- if (dumpPackage == null) {
- // If we are not filtering by package, we want to print absolutely everything,
- // so always print the header even if there are no activities inside.
- headerPrinter.run();
- headerPrinter = null;
+ if (dumpAll) {
+ printThisActivity(pw, mLastPausedActivity, dumpPackage, false,
+ prefix + " mLastPausedActivity: ", null);
+ }
+ if (isLeafTask()) {
+ pw.println(prefix + " isSleeping=" + shouldSleepActivities());
+ printThisActivity(pw, getTopPausingActivity(), dumpPackage, false,
+ prefix + " topPausingActivity=", null);
+ printThisActivity(pw, getTopResumedActivity(), dumpPackage, false,
+ prefix + " topResumedActivity=", null);
+ if (mMinWidth != INVALID_MIN_SIZE || mMinHeight != INVALID_MIN_SIZE) {
+ pw.print(prefix); pw.print(" mMinWidth="); pw.print(mMinWidth);
+ pw.print(" mMinHeight="); pw.println(mMinHeight);
}
- final ArrayList<ActivityRecord> activities = new ArrayList<>();
- // Add activities by traversing the hierarchy from bottom to top, since activities
- // are dumped in reverse order in {@link ActivityTaskSupervisor#dumpHistoryList()}.
- task.forAllActivities((Consumer<ActivityRecord>) activities::add,
- false /* traverseTopToBottom */);
- dumpHistoryList(fd, pw, activities, prefix, "Hist", true, !dumpAll, dumpClient,
- dumpPackage, false, headerPrinter, task);
- }, true /* traverseTopToBottom */);
- return printed.get();
+ }
}
ArrayList<ActivityRecord> getDumpActivitiesLocked(String name) {
@@ -7780,6 +6018,7 @@
return mAnimatingActivityRegistry;
}
+ @Override
void executeAppTransition(ActivityOptions options) {
mDisplayContent.executeAppTransition();
ActivityOptions.abort(options);
@@ -7802,10 +6041,6 @@
return display != null ? display.isSleeping() : mAtmService.isSleepingLocked();
}
- boolean shouldSleepOrShutDownActivities() {
- return shouldSleepActivities() || mAtmService.mShuttingDown;
- }
-
private Rect getRawBounds() {
return super.getBounds();
}
@@ -7830,8 +6065,8 @@
proto.write(DISPLAY_ID, getDisplayId());
proto.write(ROOT_TASK_ID, getRootTaskId());
- if (mResumedActivity != null) {
- mResumedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
+ if (getTopResumedActivity() != null) {
+ getTopResumedActivity().writeIdentifierToProto(proto, RESUMED_ACTIVITY);
}
if (realActivity != null) {
proto.write(REAL_ACTIVITY, realActivity.flattenToShortString());
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 90d40f3..5635adb 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -34,10 +34,10 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskManagerService.TAG_ROOT_TASK;
import static com.android.server.wm.DisplayContent.alwaysCreateRootTask;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
-import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -467,7 +467,7 @@
// Update the top resumed activity because the preferred top focusable task may be changed.
mAtmService.mTaskSupervisor.updateTopResumedActivityIfNeeded();
- final ActivityRecord r = child.getResumedActivity();
+ final ActivityRecord r = child.getTopResumedActivity();
if (r != null && r == mRootWindowContainer.getTopResumedActivity()) {
mAtmService.setResumedActivityUncheckLocked(r, "positionChildAt");
}
@@ -1229,7 +1229,7 @@
+ adjacentFlagRootTask);
}
- if (adjacentFlagRootTask.mAdjacentTask == null) {
+ if (adjacentFlagRootTask.getAdjacentTaskFragment() == null) {
throw new UnsupportedOperationException(
"Can't set non-adjacent root as launch adjacent flag root tr="
+ adjacentFlagRootTask);
@@ -1267,8 +1267,8 @@
// If the adjacent launch is coming from the same root, launch to adjacent root instead.
if (sourceTask != null
&& sourceTask.getRootTask().mTaskId == mLaunchAdjacentFlagRootTask.mTaskId
- && mLaunchAdjacentFlagRootTask.mAdjacentTask != null) {
- return mLaunchAdjacentFlagRootTask.mAdjacentTask;
+ && mLaunchAdjacentFlagRootTask.getAdjacentTaskFragment() != null) {
+ return mLaunchAdjacentFlagRootTask.getAdjacentTaskFragment().asTask();
} else {
return mLaunchAdjacentFlagRootTask;
}
@@ -1278,8 +1278,10 @@
if (mLaunchRootTasks.get(i).contains(windowingMode, activityType)) {
final Task launchRootTask = mLaunchRootTasks.get(i).task;
// Return the focusable root task for improving the UX with staged split screen.
- final Task adjacentRootTask = launchRootTask != null
- ? launchRootTask.mAdjacentTask : null;
+ final TaskFragment adjacentTaskFragment = launchRootTask != null
+ ? launchRootTask.getAdjacentTaskFragment() : null;
+ final Task adjacentRootTask =
+ adjacentTaskFragment != null ? adjacentTaskFragment.asTask() : null;
if (adjacentRootTask != null && adjacentRootTask.isFocusedRootTaskOnDisplay()) {
return adjacentRootTask;
} else {
@@ -1371,11 +1373,11 @@
}
// TODO(b/111541062): Move this into Task#getResumedActivity()
// Check if the focused root task has the resumed activity
- ActivityRecord resumedActivity = focusedRootTask.getResumedActivity();
+ ActivityRecord resumedActivity = focusedRootTask.getTopResumedActivity();
if (resumedActivity == null || resumedActivity.app == null) {
// If there is no registered resumed activity in the root task or it is not running -
// try to use previously resumed one.
- resumedActivity = focusedRootTask.getPausingActivity();
+ resumedActivity = focusedRootTask.getTopPausingActivity();
if (resumedActivity == null || resumedActivity.app == null) {
// If previously resumed activity doesn't work either - find the topmost running
// activity that can be focused.
@@ -1423,7 +1425,7 @@
continue;
}
- final ActivityRecord r = mChildren.get(i).asTask().getResumedActivity();
+ final ActivityRecord r = mChildren.get(i).asTask().getTopResumedActivity();
if (r != null && !r.isState(RESUMED)) {
return false;
}
@@ -1449,14 +1451,14 @@
*/
boolean pauseBackTasks(ActivityRecord resuming) {
final int[] someActivityPaused = {0};
- forAllLeafTasks((task) -> {
- final ActivityRecord resumedActivity = task.getResumedActivity();
+ forAllLeafTaskFragments((taskFragment) -> {
+ final ActivityRecord resumedActivity = taskFragment.getResumedActivity();
if (resumedActivity != null
- && (task.getVisibility(resuming) != TASK_VISIBILITY_VISIBLE
- || !task.isTopActivityFocusable())) {
- ProtoLog.d(WM_DEBUG_STATES, "pauseBackTasks: task=%s "
- + "mResumedActivity=%s", task, resumedActivity);
- if (task.startPausingLocked(false /* uiSleeping*/,
+ && (taskFragment.getVisibility(resuming) != TASK_FRAGMENT_VISIBILITY_VISIBLE
+ || !taskFragment.isTopActivityFocusable())) {
+ ProtoLog.d(WM_DEBUG_STATES, "pauseBackTasks: taskFrag=%s "
+ + "mResumedActivity=%s", taskFragment, resumedActivity);
+ if (taskFragment.startPausing(false /* uiSleeping*/,
resuming, "pauseBackTasks")) {
someActivityPaused[0]++;
}
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
new file mode 100644
index 0000000..b83029d
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -0,0 +1,1932 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
+import static android.view.Display.INVALID_DISPLAY;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
+import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_OPEN;
+
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TRANSITION;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TRANSITION;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_USER_LEAVING;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityOptions;
+import android.app.ResultInfo;
+import android.app.WindowConfiguration;
+import android.app.servertransaction.ActivityResultItem;
+import android.app.servertransaction.ClientTransaction;
+import android.app.servertransaction.NewIntentItem;
+import android.app.servertransaction.PauseActivityItem;
+import android.app.servertransaction.ResumeActivityItem;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.RemoteException;
+import android.util.DisplayMetrics;
+import android.util.Slog;
+import android.view.DisplayInfo;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.util.function.pooled.PooledFunction;
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.internal.util.function.pooled.PooledPredicate;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/**
+ * A basic container that can be used to contain activities or other {@link TaskFragment}, which
+ * also able to manage the activity lifecycle and updates the visibilities of the activities in it.
+ */
+class TaskFragment extends WindowContainer<WindowContainer> {
+ @IntDef(prefix = {"TASK_FRAGMENT_VISIBILITY"}, value = {
+ TASK_FRAGMENT_VISIBILITY_VISIBLE,
+ TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ TASK_FRAGMENT_VISIBILITY_INVISIBLE,
+ })
+ @interface TaskFragmentVisibility {}
+
+ /**
+ * TaskFragment is visible. No other TaskFragment(s) on top that fully or partially occlude it.
+ */
+ static final int TASK_FRAGMENT_VISIBILITY_VISIBLE = 0;
+
+ /** TaskFragment is partially occluded by other translucent TaskFragment(s) on top of it. */
+ static final int TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT = 1;
+
+ /** TaskFragment is completely invisible. */
+ static final int TASK_FRAGMENT_VISIBILITY_INVISIBLE = 2;
+
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskFragment" : TAG_ATM;
+ private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
+ private static final String TAG_RESULTS = TAG + POSTFIX_RESULTS;
+ private static final String TAG_TRANSITION = TAG + POSTFIX_TRANSITION;
+ private static final String TAG_USER_LEAVING = TAG + POSTFIX_USER_LEAVING;
+
+ /** Set to false to disable the preview that is shown while a new activity is being started. */
+ static final boolean SHOW_APP_STARTING_PREVIEW = true;
+
+ /**
+ * Indicate that the minimal width/height should use the default value.
+ *
+ * @see #mMinWidth
+ * @see #mMinHeight
+ */
+ static final int INVALID_MIN_SIZE = -1;
+
+ final ActivityTaskManagerService mAtmService;
+ final ActivityTaskSupervisor mTaskSupervisor;
+ final RootWindowContainer mRootWindowContainer;
+
+ // TODO(b/189384393): this is not set in TaskFragment so far. It should be passed from the
+ // parent task when adding to the hierarchy
+ /**
+ * Minimal width of this task fragment when it's resizeable. {@link #INVALID_MIN_SIZE} means it
+ * should use the default minimal width.
+ */
+ int mMinWidth;
+ // TODO(b/189384393): this is not set in TaskFragment so far. It should be passed from the
+ // parent task when adding to the hierarchy
+ /**
+ * Minimal height of this task fragment when it's resizeable. {@link #INVALID_MIN_SIZE} means it
+ * should use the default minimal height.
+ */
+ int mMinHeight;
+
+ // The TaskFragment that adjacent to this one.
+ private TaskFragment mAdjacentTaskFragment;
+
+ /**
+ * When we are in the process of pausing an activity, before starting the
+ * next one, this variable holds the activity that is currently being paused.
+ *
+ * Only set at leaf task fragments.
+ */
+ @Nullable
+ private ActivityRecord mPausingActivity = null;
+
+ /**
+ * This is the last activity that we put into the paused state. This is
+ * used to determine if we need to do an activity transition while sleeping,
+ * when we normally hold the top activity paused.
+ */
+ // TODO(b/189384393): check whether this should work on root task or leaf.
+ ActivityRecord mLastPausedActivity = null;
+
+ /**
+ * Current activity that is resumed, or null if there is none.
+ * Only set at leaf task fragments.
+ */
+ @Nullable
+ private ActivityRecord mResumedActivity = null;
+
+ private final Rect mTmpInsets = new Rect();
+ private final Rect mTmpBounds = new Rect();
+ private final Rect mTmpFullBounds = new Rect();
+ private final Rect mTmpStableBounds = new Rect();
+ private final Rect mTmpNonDecorBounds = new Rect();
+
+ private final EnsureActivitiesVisibleHelper mEnsureActivitiesVisibleHelper =
+ new EnsureActivitiesVisibleHelper(this);
+ private final EnsureVisibleActivitiesConfigHelper mEnsureVisibleActivitiesConfigHelper =
+ new EnsureVisibleActivitiesConfigHelper();
+ private class EnsureVisibleActivitiesConfigHelper {
+ private boolean mUpdateConfig;
+ private boolean mPreserveWindow;
+ private boolean mBehindFullscreen;
+
+ void reset(boolean preserveWindow) {
+ mPreserveWindow = preserveWindow;
+ mUpdateConfig = false;
+ mBehindFullscreen = false;
+ }
+
+ void process(ActivityRecord start, boolean preserveWindow) {
+ if (start == null || !start.mVisibleRequested) {
+ return;
+ }
+ reset(preserveWindow);
+
+ final PooledFunction f = PooledLambda.obtainFunction(
+ EnsureVisibleActivitiesConfigHelper::processActivity, this,
+ PooledLambda.__(ActivityRecord.class));
+ forAllActivities(f, start, true /*includeBoundary*/, true /*traverseTopToBottom*/);
+ f.recycle();
+
+ if (mUpdateConfig) {
+ // TODO(b/189384393): rename to resumeFocusedTaskFragmentsTopActivities
+ // Ensure the resumed state of the focus activity if we updated the configuration of
+ // any activity.
+ mRootWindowContainer.resumeFocusedTasksTopActivities();
+ }
+ }
+
+ boolean processActivity(ActivityRecord r) {
+ mUpdateConfig |= r.ensureActivityConfiguration(0 /*globalChanges*/, mPreserveWindow);
+ mBehindFullscreen |= r.occludesParent();
+ return mBehindFullscreen;
+ }
+ }
+
+ TaskFragment(ActivityTaskManagerService atmService) {
+ super(atmService.mWindowManager);
+
+ mAtmService = atmService;
+ mTaskSupervisor = atmService.mTaskSupervisor;
+ mRootWindowContainer = mAtmService.mRootWindowContainer;
+ }
+
+ void setAdjacentTaskFragment(TaskFragment taskFragment) {
+ mAdjacentTaskFragment = taskFragment;
+ taskFragment.mAdjacentTaskFragment = this;
+ }
+
+ TaskFragment getAdjacentTaskFragment() {
+ return mAdjacentTaskFragment;
+ }
+
+ /** @return the currently resumed activity. */
+ ActivityRecord getResumedActivity() {
+ return mResumedActivity;
+ }
+
+ void setResumedActivity(ActivityRecord r, String reason) {
+ warnForNonLeafTaskFragment("setResumedActivity");
+ if (mResumedActivity == r) {
+ return;
+ }
+
+ if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) {
+ Slog.d(TAG, "setResumedActivity taskFrag:" + this + " + from: "
+ + mResumedActivity + " to:" + r + " reason:" + reason);
+ }
+ mResumedActivity = r;
+ mTaskSupervisor.updateTopResumedActivityIfNeeded();
+ }
+
+ @VisibleForTesting
+ void setPausingActivity(ActivityRecord pausing) {
+ mPausingActivity = pausing;
+ }
+
+ ActivityRecord getPausingActivity() {
+ return mPausingActivity;
+ }
+
+ int getDisplayId() {
+ final DisplayContent dc = getDisplayContent();
+ return dc != null ? dc.mDisplayId : INVALID_DISPLAY;
+ }
+
+ @Nullable
+ Task getTask() {
+ if (asTask() != null) {
+ return asTask();
+ }
+
+ TaskFragment parent = getParent() != null ? getParent().asTaskFragment() : null;
+ return parent != null ? parent.getTask() : null;
+ }
+
+ @Override
+ @Nullable
+ TaskDisplayArea getDisplayArea() {
+ return (TaskDisplayArea) super.getDisplayArea();
+ }
+
+ @Override
+ public boolean isAttached() {
+ final TaskDisplayArea taskDisplayArea = getDisplayArea();
+ return taskDisplayArea != null && !taskDisplayArea.isRemoved();
+ }
+
+ /**
+ * Returns the root {@link TaskFragment}, which is usually also a {@link Task}.
+ */
+ @NonNull
+ TaskFragment getRootTaskFragment() {
+ final WindowContainer parent = getParent();
+ if (parent == null) return this;
+
+ final TaskFragment parentTaskFragment = parent.asTaskFragment();
+ return parentTaskFragment == null ? this : parentTaskFragment.getRootTaskFragment();
+ }
+
+ @Override
+ TaskFragment asTaskFragment() {
+ return this;
+ }
+
+
+ /**
+ * Simply check and give warning logs if this is not operated on leaf {@link TaskFragment}.
+ */
+ private void warnForNonLeafTaskFragment(String func) {
+ if (!isLeafTaskFragment()) {
+ Slog.w(TAG, func + " on non-leaf task fragment " + this);
+ }
+ }
+
+ boolean hasDirectChildActivities() {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ if (mChildren.get(i).asActivityRecord() != null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void cleanUpActivityReferences(ActivityRecord r) {
+ if (mPausingActivity != null && mPausingActivity == r) {
+ mPausingActivity = null;
+ }
+
+ if (mResumedActivity != null && mResumedActivity == r) {
+ setResumedActivity(null, "cleanUpActivityReferences");
+ }
+
+ // TODO(b/189384393): why clean up the references in parents while the references are
+ // kept in the leaf?
+ final WindowContainer parent = getParent();
+ if (parent != null && parent.asTaskFragment() != null) {
+ parent.asTaskFragment().cleanUpActivityReferences(r);
+ return;
+ }
+ r.removeTimeouts();
+ }
+
+ /**
+ * Returns whether this TaskFragment is currently forced to be hidden for any reason.
+ */
+ protected boolean isForceHidden() {
+ return false;
+ }
+
+ boolean isLeafTaskFragment() {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ if (mChildren.get(i).asTaskFragment() != null) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * This should be called when an child activity changes state. This should only
+ * be called from
+ * {@link ActivityRecord#setState(ActivityRecord.State, String)} .
+ * @param record The {@link ActivityRecord} whose state has changed.
+ * @param state The new state.
+ * @param reason The reason for the change.
+ */
+ void onActivityStateChanged(ActivityRecord record, ActivityRecord.State state,
+ String reason) {
+ warnForNonLeafTaskFragment("onActivityStateChanged");
+ if (record == mResumedActivity && state != RESUMED) {
+ setResumedActivity(null, reason + " - onActivityStateChanged");
+ }
+
+ if (state == RESUMED) {
+ if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) {
+ Slog.v(TAG, "set resumed activity to:" + record + " reason:" + reason);
+ }
+ setResumedActivity(record, reason + " - onActivityStateChanged");
+ if (record == mRootWindowContainer.getTopResumedActivity()) {
+ mAtmService.setResumedActivityUncheckLocked(record, reason);
+ }
+ mTaskSupervisor.mRecentTasks.add(record.getTask());
+ }
+ }
+
+ /**
+ * Resets local parameters because an app's activity died.
+ * @param app The app of the activity that died.
+ * @return {@code true} if the process of the pausing activity is died.
+ */
+ boolean handleAppDied(WindowProcessController app) {
+ warnForNonLeafTaskFragment("handleAppDied");
+ boolean isPausingDied = false;
+ if (mPausingActivity != null && mPausingActivity.app == app) {
+ ProtoLog.v(WM_DEBUG_STATES, "App died while pausing: %s",
+ mPausingActivity);
+ mPausingActivity = null;
+ isPausingDied = true;
+ }
+ if (mLastPausedActivity != null && mLastPausedActivity.app == app) {
+ if (mLastPausedActivity.isNoHistory()) {
+ mTaskSupervisor.mNoHistoryActivities.remove(mLastPausedActivity);
+ }
+ mLastPausedActivity = null;
+ }
+ return isPausingDied;
+ }
+
+ void awakeFromSleeping() {
+ if (mPausingActivity != null) {
+ Slog.d(TAG, "awakeFromSleeping: previously pausing activity didn't pause");
+ mPausingActivity.activityPaused(true);
+ }
+ }
+
+ /**
+ * Tries to put the activities in the task fragment to sleep.
+ *
+ * If the task fragment is not in a state where its activities can be put to sleep, this
+ * function will start any necessary actions to move the task fragment into such a state.
+ * It is expected that this function get called again when those actions complete.
+ *
+ * @param shuttingDown {@code true} when the called because the device is shutting down.
+ * @return true if the root task finished going to sleep, false if the root task only started
+ * the process of going to sleep (checkReadyForSleep will be called when that process finishes).
+ */
+ boolean sleepIfPossible(boolean shuttingDown) {
+ boolean shouldSleep = true;
+ if (mResumedActivity != null) {
+ // Still have something resumed; can't sleep until it is paused.
+ ProtoLog.v(WM_DEBUG_STATES, "Sleep needs to pause %s", mResumedActivity);
+ startPausing(false /* userLeaving */, true /* uiSleeping */, null /* resuming */,
+ "sleep");
+ shouldSleep = false;
+ } else if (mPausingActivity != null) {
+ // Still waiting for something to pause; can't sleep yet.
+ ProtoLog.v(WM_DEBUG_STATES, "Sleep still waiting to pause %s", mPausingActivity);
+ shouldSleep = false;
+ }
+
+ if (!shuttingDown) {
+ if (containsStoppingActivity()) {
+ // Still need to tell some activities to stop; can't sleep yet.
+ ProtoLog.v(WM_DEBUG_STATES, "Sleep still need to stop %d activities",
+ mTaskSupervisor.mStoppingActivities.size());
+
+ mTaskSupervisor.scheduleIdle();
+ shouldSleep = false;
+ }
+ }
+
+ if (shouldSleep) {
+ updateActivityVisibilities(null /* starting */, 0 /* configChanges */,
+ !PRESERVE_WINDOWS, true /* notifyClients */);
+ }
+
+ return shouldSleep;
+ }
+
+ private boolean containsStoppingActivity() {
+ for (int i = mTaskSupervisor.mStoppingActivities.size() - 1; i >= 0; --i) {
+ ActivityRecord r = mTaskSupervisor.mStoppingActivities.get(i);
+ if (r.getTaskFragment() == this) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if the TaskFragment is translucent and can have other contents visible behind
+ * it if needed. A TaskFragment is considered translucent if it don't contain a visible or
+ * starting (about to be visible) activity that is fullscreen (opaque).
+ * @param starting The currently starting activity or null if there is none.
+ */
+ @VisibleForTesting
+ boolean isTranslucent(ActivityRecord starting) {
+ if (!isAttached() || isForceHidden()) {
+ return true;
+ }
+ final PooledPredicate p = PooledLambda.obtainPredicate(TaskFragment::isOpaqueActivity,
+ PooledLambda.__(ActivityRecord.class), starting);
+ final ActivityRecord opaque = getActivity(p);
+ p.recycle();
+ return opaque == null;
+ }
+
+ private static boolean isOpaqueActivity(ActivityRecord r, ActivityRecord starting) {
+ if (r.finishing) {
+ // We don't factor in finishing activities when determining translucency since
+ // they will be gone soon.
+ return false;
+ }
+
+ if (!r.visibleIgnoringKeyguard && r != starting) {
+ // Also ignore invisible activities that are not the currently starting
+ // activity (about to be visible).
+ return false;
+ }
+
+ if (r.occludesParent()) {
+ // Root task isn't translucent if it has at least one fullscreen activity
+ // that is visible.
+ return true;
+ }
+ return false;
+ }
+
+ ActivityRecord topRunningActivity() {
+ return topRunningActivity(false /* focusableOnly */);
+ }
+
+ ActivityRecord topRunningActivity(boolean focusableOnly) {
+ // Split into 2 to avoid object creation due to variable capture.
+ if (focusableOnly) {
+ return getActivity((r) -> r.canBeTopRunning() && r.isFocusable());
+ } else {
+ return getActivity(ActivityRecord::canBeTopRunning);
+ }
+ }
+
+ boolean isTopActivityFocusable() {
+ final ActivityRecord r = topRunningActivity();
+ return r != null ? r.isFocusable()
+ : (isFocusable() && getWindowConfiguration().canReceiveKeys());
+ }
+
+ /**
+ * Returns the visibility state of this TaskFragment.
+ *
+ * @param starting The currently starting activity or null if there is none.
+ */
+ @TaskFragmentVisibility
+ int getVisibility(ActivityRecord starting) {
+ if (!isAttached() || isForceHidden()) {
+ return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ }
+
+ if (isTopActivityLaunchedBehind()) {
+ return TASK_FRAGMENT_VISIBILITY_VISIBLE;
+ }
+
+ boolean gotRootSplitScreenFragment = false;
+ boolean gotOpaqueSplitScreenPrimary = false;
+ boolean gotOpaqueSplitScreenSecondary = false;
+ boolean gotTranslucentFullscreen = false;
+ boolean gotTranslucentSplitScreenPrimary = false;
+ boolean gotTranslucentSplitScreenSecondary = false;
+ boolean shouldBeVisible = true;
+
+ // This TaskFragment is only considered visible if all its parent TaskFragments are
+ // considered visible, so check the visibility of all ancestor TaskFragment first.
+ final WindowContainer parent = getParent();
+ if (parent.asTaskFragment() != null) {
+ final int parentVisibility = parent.asTaskFragment().getVisibility(starting);
+ if (parentVisibility == TASK_FRAGMENT_VISIBILITY_INVISIBLE) {
+ // Can't be visible if parent isn't visible
+ return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ } else if (parentVisibility == TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT) {
+ // Parent is behind a translucent container so the highest visibility this container
+ // can get is that.
+ gotTranslucentFullscreen = true;
+ }
+ }
+
+ final List<TaskFragment> adjacentTaskFragments = new ArrayList<>();
+ final int windowingMode = getWindowingMode();
+ final boolean isAssistantType = isActivityTypeAssistant();
+ for (int i = parent.getChildCount() - 1; i >= 0; --i) {
+ final WindowContainer wc = parent.getChildAt(i);
+ final TaskFragment other = wc.asTaskFragment();
+ if (other == null) continue;
+
+ final boolean hasRunningActivities = other.topRunningActivity() != null;
+ if (other == this) {
+ // Should be visible if there is no other fragment occluding it, unless it doesn't
+ // have any running activities, not starting one and not home stack.
+ shouldBeVisible = hasRunningActivities
+ || (starting != null && starting.isDescendantOf(this))
+ || isActivityTypeHome();
+ break;
+ }
+
+ if (!hasRunningActivities) {
+ continue;
+ }
+
+ final int otherWindowingMode = other.getWindowingMode();
+ if (otherWindowingMode == WINDOWING_MODE_FULLSCREEN) {
+ if (other.isTranslucent(starting)) {
+ // Can be visible behind a translucent fullscreen TaskFragment.
+ gotTranslucentFullscreen = true;
+ continue;
+ }
+ return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ } else if (otherWindowingMode == WINDOWING_MODE_MULTI_WINDOW
+ && other.matchParentBounds()) {
+ if (other.isTranslucent(starting)) {
+ // Can be visible behind a translucent TaskFragment.
+ gotTranslucentFullscreen = true;
+ continue;
+ }
+ // Multi-window TaskFragment that matches parent bounds would occlude other children
+ return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ && !gotOpaqueSplitScreenPrimary) {
+ gotRootSplitScreenFragment = true;
+ gotTranslucentSplitScreenPrimary = other.isTranslucent(starting);
+ gotOpaqueSplitScreenPrimary = !gotTranslucentSplitScreenPrimary;
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ && gotOpaqueSplitScreenPrimary) {
+ // Can't be visible behind another opaque TaskFragment in split-screen-primary.
+ return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ }
+ } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+ && !gotOpaqueSplitScreenSecondary) {
+ gotRootSplitScreenFragment = true;
+ gotTranslucentSplitScreenSecondary = other.isTranslucent(starting);
+ gotOpaqueSplitScreenSecondary = !gotTranslucentSplitScreenSecondary;
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+ && gotOpaqueSplitScreenSecondary) {
+ // Can't be visible behind another opaque TaskFragment in split-screen-secondary
+ return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ }
+ }
+ if (gotOpaqueSplitScreenPrimary && gotOpaqueSplitScreenSecondary) {
+ // Can not be visible if we are in split-screen windowing mode and both halves of
+ // the screen are opaque.
+ return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ }
+ if (isAssistantType && gotRootSplitScreenFragment) {
+ // Assistant TaskFragment can't be visible behind split-screen. In addition to
+ // this not making sense, it also works around an issue here we boost the z-order
+ // of the assistant window surfaces in window manager whenever it is visible.
+ return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ }
+
+ if (other.mAdjacentTaskFragment != null) {
+ if (adjacentTaskFragments.contains(other.mAdjacentTaskFragment)) {
+ if (other.isTranslucent(starting)
+ || other.mAdjacentTaskFragment.isTranslucent(starting)) {
+ // Can be visible behind a translucent adjacent TaskFragments.
+ gotTranslucentFullscreen = true;
+ continue;
+ }
+ // Can not be visible behind adjacent TaskFragments.
+ return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ } else {
+ adjacentTaskFragments.add(other);
+ }
+ }
+
+ }
+
+ if (!shouldBeVisible) {
+ return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ }
+
+ // Handle cases when there can be a translucent split-screen TaskFragment on top.
+ switch (windowingMode) {
+ case WINDOWING_MODE_FULLSCREEN:
+ if (gotTranslucentSplitScreenPrimary || gotTranslucentSplitScreenSecondary) {
+ // At least one of the split-screen TaskFragment that covers this one is
+ // translucent.
+ // When in split mode, home will be reparented to the secondary split while
+ // leaving TaskFragments not supporting split below. Due to
+ // TaskDisplayArea#assignRootTaskOrdering always adjusts home surface layer to
+ // the bottom, this makes sure TaskFragments not in split roots won't occlude
+ // home task unexpectedly.
+ return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ }
+ break;
+ case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
+ if (gotTranslucentSplitScreenPrimary) {
+ // Covered by translucent primary split-screen on top.
+ return TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
+ }
+ break;
+ case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY:
+ if (gotTranslucentSplitScreenSecondary) {
+ // Covered by translucent secondary split-screen on top.
+ return TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
+ }
+ break;
+ }
+
+ // Lastly - check if there is a translucent fullscreen TaskFragment on top.
+ return gotTranslucentFullscreen
+ ? TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT
+ : TASK_FRAGMENT_VISIBILITY_VISIBLE;
+ }
+
+ private boolean isTopActivityLaunchedBehind() {
+ final ActivityRecord top = topRunningActivity();
+ if (top != null && top.mLaunchTaskBehind) {
+ return true;
+ }
+ return false;
+ }
+
+ final void updateActivityVisibilities(@Nullable ActivityRecord starting, int configChanges,
+ boolean preserveWindows, boolean notifyClients) {
+ mTaskSupervisor.beginActivityVisibilityUpdate();
+ try {
+ mEnsureActivitiesVisibleHelper.process(
+ starting, configChanges, preserveWindows, notifyClients);
+ } finally {
+ mTaskSupervisor.endActivityVisibilityUpdate();
+ }
+ }
+
+ final boolean resumeTopActivity(ActivityRecord prev, ActivityOptions options,
+ boolean deferPause) {
+ ActivityRecord next = topRunningActivity(true /* focusableOnly */);
+ if (next == null || !next.canResumeByCompat()) {
+ return false;
+ }
+
+ next.delayedResume = false;
+ final TaskDisplayArea taskDisplayArea = getDisplayArea();
+
+ // If the top activity is the resumed one, nothing to do.
+ if (mResumedActivity == next && next.isState(RESUMED)
+ && taskDisplayArea.allResumedActivitiesComplete()) {
+ // Make sure we have executed any pending transitions, since there
+ // should be nothing left to do at this point.
+ executeAppTransition(options);
+ // For devices that are not in fullscreen mode (e.g. freeform windows), it's possible
+ // we still want to check if the visibility of other windows have changed (e.g. bringing
+ // a fullscreen window forward to cover another freeform activity.)
+ if (taskDisplayArea.inMultiWindowMode()) {
+ taskDisplayArea.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
+ false /* preserveWindows */, true /* notifyClients */);
+ }
+ ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Top activity "
+ + "resumed %s", next);
+ return false;
+ }
+
+ // If we are currently pausing an activity, then don't do anything until that is done.
+ final boolean allPausedComplete = mRootWindowContainer.allPausedActivitiesComplete();
+ if (!allPausedComplete) {
+ ProtoLog.v(WM_DEBUG_STATES,
+ "resumeTopActivity: Skip resume: some activity pausing.");
+ return false;
+ }
+
+ // If we are sleeping, and there is no resumed activity, and the top activity is paused,
+ // well that is the state we want.
+ if (mLastPausedActivity == next && shouldSleepOrShutDownActivities()) {
+ // Make sure we have executed any pending transitions, since there
+ // should be nothing left to do at this point.
+ executeAppTransition(options);
+ ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Going to sleep and"
+ + " all paused");
+ return false;
+ }
+
+ // Make sure that the user who owns this activity is started. If not,
+ // we will just leave it as is because someone should be bringing
+ // another user's activities to the top of the stack.
+ if (!mAtmService.mAmInternal.hasStartedUserState(next.mUserId)) {
+ Slog.w(TAG, "Skipping resume of top activity " + next
+ + ": user " + next.mUserId + " is stopped");
+ return false;
+ }
+
+ // The activity may be waiting for stop, but that is no longer
+ // appropriate for it.
+ mTaskSupervisor.mStoppingActivities.remove(next);
+
+ if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next);
+
+ mTaskSupervisor.setLaunchSource(next.info.applicationInfo.uid);
+
+ ActivityRecord lastResumed = null;
+ final Task lastFocusedRootTask = taskDisplayArea.getLastFocusedRootTask();
+ if (lastFocusedRootTask != null && lastFocusedRootTask != getRootTaskFragment().asTask()) {
+ // So, why aren't we using prev here??? See the param comment on the method. prev
+ // doesn't represent the last resumed activity. However, the last focus stack does if
+ // it isn't null.
+ lastResumed = lastFocusedRootTask.getTopResumedActivity();
+ }
+
+ boolean pausing = !deferPause && taskDisplayArea.pauseBackTasks(next);
+ if (mResumedActivity != null) {
+ ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Pausing %s", mResumedActivity);
+ pausing |= startPausingInner(mTaskSupervisor.mUserLeaving, false /* uiSleeping */,
+ next, "resumeTopActivity");
+ }
+ if (pausing) {
+ ProtoLog.v(WM_DEBUG_STATES, "resumeTopActivity: Skip resume: need to"
+ + " start pausing");
+ // At this point we want to put the upcoming activity's process
+ // at the top of the LRU list, since we know we will be needing it
+ // very soon and it would be a waste to let it get killed if it
+ // happens to be sitting towards the end.
+ if (next.attachedToProcess()) {
+ next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,
+ true /* activityChange */, false /* updateOomAdj */,
+ false /* addPendingTopUid */);
+ } else if (!next.isProcessRunning()) {
+ // Since the start-process is asynchronous, if we already know the process of next
+ // activity isn't running, we can start the process earlier to save the time to wait
+ // for the current activity to be paused.
+ final boolean isTop = this == taskDisplayArea.getFocusedRootTask();
+ mAtmService.startProcessAsync(next, false /* knownToBeDead */, isTop,
+ isTop ? "pre-top-activity" : "pre-activity");
+ }
+ if (lastResumed != null) {
+ lastResumed.setWillCloseOrEnterPip(true);
+ }
+ return true;
+ } else if (mResumedActivity == next && next.isState(RESUMED)
+ && taskDisplayArea.allResumedActivitiesComplete()) {
+ // It is possible for the activity to be resumed when we paused back stacks above if the
+ // next activity doesn't have to wait for pause to complete.
+ // So, nothing else to-do except:
+ // Make sure we have executed any pending transitions, since there
+ // should be nothing left to do at this point.
+ executeAppTransition(options);
+ ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Top activity resumed "
+ + "(dontWaitForPause) %s", next);
+ return true;
+ }
+
+ // If the most recent activity was noHistory but was only stopped rather
+ // than stopped+finished because the device went to sleep, we need to make
+ // sure to finish it as we're making a new activity topmost.
+ if (shouldSleepActivities()) {
+ mTaskSupervisor.finishNoHistoryActivitiesIfNeeded(next);
+ }
+
+ if (prev != null && prev != next && next.nowVisible) {
+ // The next activity is already visible, so hide the previous
+ // activity's windows right now so we can show the new one ASAP.
+ // We only do this if the previous is finishing, which should mean
+ // it is on top of the one being resumed so hiding it quickly
+ // is good. Otherwise, we want to do the normal route of allowing
+ // the resumed activity to be shown so we can decide if the
+ // previous should actually be hidden depending on whether the
+ // new one is found to be full-screen or not.
+ if (prev.finishing) {
+ prev.setVisibility(false);
+ if (DEBUG_SWITCH) {
+ Slog.v(TAG_SWITCH, "Not waiting for visible to hide: " + prev
+ + ", nowVisible=" + next.nowVisible);
+ }
+ } else {
+ if (DEBUG_SWITCH) {
+ Slog.v(TAG_SWITCH, "Previous already visible but still waiting to hide: " + prev
+ + ", nowVisible=" + next.nowVisible);
+ }
+ }
+ }
+
+ // Launching this app's activity, make sure the app is no longer
+ // considered stopped.
+ try {
+ mTaskSupervisor.getActivityMetricsLogger()
+ .notifyBeforePackageUnstopped(next.packageName);
+ mAtmService.getPackageManager().setPackageStoppedState(
+ next.packageName, false, next.mUserId); /* TODO: Verify if correct userid */
+ } catch (RemoteException e1) {
+ } catch (IllegalArgumentException e) {
+ Slog.w(TAG, "Failed trying to unstop package "
+ + next.packageName + ": " + e);
+ }
+
+ // We are starting up the next activity, so tell the window manager
+ // that the previous one will be hidden soon. This way it can know
+ // to ignore it when computing the desired screen orientation.
+ boolean anim = true;
+ final DisplayContent dc = taskDisplayArea.mDisplayContent;
+ if (prev != null) {
+ if (prev.finishing) {
+ if (DEBUG_TRANSITION) {
+ Slog.v(TAG_TRANSITION, "Prepare close transition: prev=" + prev);
+ }
+ if (mTaskSupervisor.mNoAnimActivities.contains(prev)) {
+ anim = false;
+ dc.prepareAppTransition(TRANSIT_NONE);
+ } else {
+ dc.prepareAppTransition(TRANSIT_CLOSE);
+ }
+ prev.setVisibility(false);
+ } else {
+ if (DEBUG_TRANSITION) {
+ Slog.v(TAG_TRANSITION, "Prepare open transition: prev=" + prev);
+ }
+ if (mTaskSupervisor.mNoAnimActivities.contains(next)) {
+ anim = false;
+ dc.prepareAppTransition(TRANSIT_NONE);
+ } else {
+ dc.prepareAppTransition(TRANSIT_OPEN,
+ next.mLaunchTaskBehind ? TRANSIT_FLAG_OPEN_BEHIND : 0);
+ }
+ }
+ } else {
+ if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: no previous");
+ if (mTaskSupervisor.mNoAnimActivities.contains(next)) {
+ anim = false;
+ dc.prepareAppTransition(TRANSIT_NONE);
+ } else {
+ dc.prepareAppTransition(TRANSIT_OPEN);
+ }
+ }
+
+ if (anim) {
+ next.applyOptionsAnimation();
+ } else {
+ next.abortAndClearOptionsAnimation();
+ }
+
+ mTaskSupervisor.mNoAnimActivities.clear();
+
+ if (next.attachedToProcess()) {
+ if (DEBUG_SWITCH) {
+ Slog.v(TAG_SWITCH, "Resume running: " + next + " stopped=" + next.stopped
+ + " visibleRequested=" + next.mVisibleRequested);
+ }
+
+ // If the previous activity is translucent, force a visibility update of
+ // the next activity, so that it's added to WM's opening app list, and
+ // transition animation can be set up properly.
+ // For example, pressing Home button with a translucent activity in focus.
+ // Launcher is already visible in this case. If we don't add it to opening
+ // apps, maybeUpdateTransitToWallpaper() will fail to identify this as a
+ // TRANSIT_WALLPAPER_OPEN animation, and run some funny animation.
+ final boolean lastActivityTranslucent = lastFocusedRootTask != null
+ && (lastFocusedRootTask.inMultiWindowMode()
+ || (lastFocusedRootTask.mLastPausedActivity != null
+ && !lastFocusedRootTask.mLastPausedActivity.occludesParent()));
+
+ // This activity is now becoming visible.
+ if (!next.mVisibleRequested || next.stopped || lastActivityTranslucent) {
+ next.setVisibility(true);
+ }
+
+ // schedule launch ticks to collect information about slow apps.
+ next.startLaunchTickingLocked();
+
+ ActivityRecord lastResumedActivity =
+ lastFocusedRootTask == null ? null
+ : lastFocusedRootTask.getTopResumedActivity();
+ final ActivityRecord.State lastState = next.getState();
+
+ mAtmService.updateCpuStats();
+
+ ProtoLog.v(WM_DEBUG_STATES, "Moving to RESUMED: %s (in existing)", next);
+
+ next.setState(RESUMED, "resumeTopActivity");
+
+ // Have the window manager re-evaluate the orientation of
+ // the screen based on the new activity order.
+ boolean notUpdated = true;
+
+ // Activity should also be visible if set mLaunchTaskBehind to true (see
+ // ActivityRecord#shouldBeVisibleIgnoringKeyguard()).
+ if (shouldBeVisible(next)) {
+ // We have special rotation behavior when here is some active activity that
+ // requests specific orientation or Keyguard is locked. Make sure all activity
+ // visibilities are set correctly as well as the transition is updated if needed
+ // to get the correct rotation behavior. Otherwise the following call to update
+ // the orientation may cause incorrect configurations delivered to client as a
+ // result of invisible window resize.
+ // TODO: Remove this once visibilities are set correctly immediately when
+ // starting an activity.
+ notUpdated = !mRootWindowContainer.ensureVisibilityAndConfig(next, getDisplayId(),
+ true /* markFrozenIfConfigChanged */, false /* deferResume */);
+ }
+
+ if (notUpdated) {
+ // The configuration update wasn't able to keep the existing
+ // instance of the activity, and instead started a new one.
+ // We should be all done, but let's just make sure our activity
+ // is still at the top and schedule another run if something
+ // weird happened.
+ ActivityRecord nextNext = topRunningActivity();
+ ProtoLog.i(WM_DEBUG_STATES, "Activity config changed during resume: "
+ + "%s, new next: %s", next, nextNext);
+ if (nextNext != next) {
+ // Do over!
+ mTaskSupervisor.scheduleResumeTopActivities();
+ }
+ if (!next.mVisibleRequested || next.stopped) {
+ next.setVisibility(true);
+ }
+ next.completeResumeLocked();
+ return true;
+ }
+
+ try {
+ final ClientTransaction transaction =
+ ClientTransaction.obtain(next.app.getThread(), next.appToken);
+ // Deliver all pending results.
+ ArrayList<ResultInfo> a = next.results;
+ if (a != null) {
+ final int size = a.size();
+ if (!next.finishing && size > 0) {
+ if (DEBUG_RESULTS) {
+ Slog.v(TAG_RESULTS, "Delivering results to " + next + ": " + a);
+ }
+ transaction.addCallback(ActivityResultItem.obtain(a));
+ }
+ }
+
+ if (next.newIntents != null) {
+ transaction.addCallback(
+ NewIntentItem.obtain(next.newIntents, true /* resume */));
+ }
+
+ // Well the app will no longer be stopped.
+ // Clear app token stopped state in window manager if needed.
+ next.notifyAppResumed(next.stopped);
+
+ EventLogTags.writeWmResumeActivity(next.mUserId, System.identityHashCode(next),
+ next.getTask().mTaskId, next.shortComponentName);
+
+ mAtmService.getAppWarningsLocked().onResumeActivity(next);
+ next.app.setPendingUiCleanAndForceProcessStateUpTo(mAtmService.mTopProcessState);
+ next.abortAndClearOptionsAnimation();
+ transaction.setLifecycleStateRequest(
+ ResumeActivityItem.obtain(next.app.getReportedProcState(),
+ dc.isNextTransitionForward()));
+ mAtmService.getLifecycleManager().scheduleTransaction(transaction);
+
+ ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Resumed %s", next);
+ } catch (Exception e) {
+ // Whoops, need to restart this activity!
+ ProtoLog.v(WM_DEBUG_STATES, "Resume failed; resetting state to %s: "
+ + "%s", lastState, next);
+ next.setState(lastState, "resumeTopActivityInnerLocked");
+
+ // lastResumedActivity being non-null implies there is a lastStack present.
+ if (lastResumedActivity != null) {
+ lastResumedActivity.setState(RESUMED, "resumeTopActivityInnerLocked");
+ }
+
+ Slog.i(TAG, "Restarting because process died: " + next);
+ if (!next.hasBeenLaunched) {
+ next.hasBeenLaunched = true;
+ } else if (SHOW_APP_STARTING_PREVIEW && lastFocusedRootTask != null
+ && lastFocusedRootTask.isTopRootTaskInDisplayArea()) {
+ next.showStartingWindow(false /* taskSwitch */);
+ }
+ mTaskSupervisor.startSpecificActivity(next, true, false);
+ return true;
+ }
+
+ // From this point on, if something goes wrong there is no way
+ // to recover the activity.
+ try {
+ next.completeResumeLocked();
+ } catch (Exception e) {
+ // If any exception gets thrown, toss away this
+ // activity and try the next one.
+ Slog.w(TAG, "Exception thrown during resume of " + next, e);
+ next.finishIfPossible("resume-exception", true /* oomAdj */);
+ return true;
+ }
+ } else {
+ // Whoops, need to restart this activity!
+ if (!next.hasBeenLaunched) {
+ next.hasBeenLaunched = true;
+ } else {
+ if (SHOW_APP_STARTING_PREVIEW) {
+ next.showStartingWindow(false /* taskSwich */);
+ }
+ if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Restarting: " + next);
+ }
+ ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Restarting %s", next);
+ mTaskSupervisor.startSpecificActivity(next, true, true);
+ }
+
+ return true;
+ }
+
+ boolean shouldSleepOrShutDownActivities() {
+ return shouldSleepActivities() || mAtmService.mShuttingDown;
+ }
+
+ /**
+ * Returns true if the TaskFragment should be visible.
+ *
+ * @param starting The currently starting activity or null if there is none.
+ */
+ boolean shouldBeVisible(ActivityRecord starting) {
+ return getVisibility(starting) != TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ }
+
+ final boolean startPausing(boolean uiSleeping, ActivityRecord resuming, String reason) {
+ return startPausing(mTaskSupervisor.mUserLeaving, uiSleeping, resuming, reason);
+ }
+
+ final boolean startPausing(boolean userLeaving, boolean uiSleeping,
+ ActivityRecord resuming, String reason) {
+ final int[] pausing = {0};
+ forAllLeafTaskFragments((f) -> {
+ if (f.startPausingInner(userLeaving, uiSleeping, resuming, reason)) {
+ pausing[0]++;
+ }
+ }, true /* traverseTopToBottom */);
+ return pausing[0] > 0;
+ }
+
+ /**
+ * Start pausing the currently resumed activity. It is an error to call this if there
+ * is already an activity being paused or there is no resumed activity.
+ *
+ * @param userLeaving True if this should result in an onUserLeaving to the current activity.
+ * @param uiSleeping True if this is happening with the user interface going to sleep (the
+ * screen turning off).
+ * @param resuming The activity we are currently trying to resume or null if this is not being
+ * called as part of resuming the top activity, so we shouldn't try to instigate
+ * a resume here if not null.
+ * @param reason The reason of pausing the activity.
+ * @return Returns true if an activity now is in the PAUSING state, and we are waiting for
+ * it to tell us when it is done.
+ */
+ private boolean startPausingInner(boolean userLeaving, boolean uiSleeping,
+ ActivityRecord resuming, String reason) {
+ if (!hasDirectChildActivities()) {
+ return false;
+ }
+
+ if (mPausingActivity != null) {
+ Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity
+ + " state=" + mPausingActivity.getState());
+ if (!shouldSleepActivities()) {
+ // Avoid recursion among check for sleep and complete pause during sleeping.
+ // Because activity will be paused immediately after resume, just let pause
+ // be completed by the order of activity paused from clients.
+ completePause(false, resuming);
+ }
+ }
+ ActivityRecord prev = mResumedActivity;
+
+ if (prev == null) {
+ if (resuming == null) {
+ Slog.wtf(TAG, "Trying to pause when nothing is resumed");
+ mRootWindowContainer.resumeFocusedTasksTopActivities();
+ }
+ return false;
+ }
+
+ if (prev == resuming) {
+ Slog.wtf(TAG, "Trying to pause activity that is in process of being resumed");
+ return false;
+ }
+
+ ProtoLog.v(WM_DEBUG_STATES, "Moving to PAUSING: %s", prev);
+ mPausingActivity = prev;
+ mLastPausedActivity = prev;
+ if (prev.isNoHistory() && !mTaskSupervisor.mNoHistoryActivities.contains(prev)) {
+ mTaskSupervisor.mNoHistoryActivities.add(prev);
+ }
+ prev.setState(PAUSING, "startPausingLocked");
+ prev.getTask().touchActiveTime();
+
+ mAtmService.updateCpuStats();
+
+ boolean pauseImmediately = false;
+ boolean shouldAutoPip = false;
+ if (resuming != null && (resuming.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0) {
+ // If the flag RESUME_WHILE_PAUSING is set, then continue to schedule the previous
+ // activity to be paused, while at the same time resuming the new resume activity
+ // only if the previous activity can't go into Pip since we want to give Pip
+ // activities a chance to enter Pip before resuming the next activity.
+ final boolean lastResumedCanPip = prev != null && prev.checkEnterPictureInPictureState(
+ "shouldResumeWhilePausing", userLeaving);
+ if (lastResumedCanPip && prev.pictureInPictureArgs.isAutoEnterEnabled()) {
+ shouldAutoPip = true;
+ } else if (!lastResumedCanPip) {
+ pauseImmediately = true;
+ } else {
+ // The previous activity may still enter PIP even though it did not allow auto-PIP.
+ }
+ }
+
+ boolean didAutoPip = false;
+ if (prev.attachedToProcess()) {
+ if (shouldAutoPip) {
+ ProtoLog.d(WM_DEBUG_STATES, "Auto-PIP allowed, entering PIP mode "
+ + "directly: %s", prev);
+
+ didAutoPip = mAtmService.enterPictureInPictureMode(prev, prev.pictureInPictureArgs);
+ mPausingActivity = null;
+ } else {
+ ProtoLog.v(WM_DEBUG_STATES, "Enqueueing pending pause: %s", prev);
+ try {
+ EventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev),
+ prev.shortComponentName, "userLeaving=" + userLeaving, reason);
+
+ mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
+ prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
+ prev.configChangeFlags, pauseImmediately));
+ } catch (Exception e) {
+ // Ignore exception, if process died other code will cleanup.
+ Slog.w(TAG, "Exception thrown during pause", e);
+ mPausingActivity = null;
+ mLastPausedActivity = null;
+ mTaskSupervisor.mNoHistoryActivities.remove(prev);
+ }
+ }
+ } else {
+ mPausingActivity = null;
+ mLastPausedActivity = null;
+ mTaskSupervisor.mNoHistoryActivities.remove(prev);
+ }
+
+ // If we are not going to sleep, we want to ensure the device is
+ // awake until the next activity is started.
+ if (!uiSleeping && !mAtmService.isSleepingOrShuttingDownLocked()) {
+ mTaskSupervisor.acquireLaunchWakelock();
+ }
+
+ // If already entered PIP mode, no need to keep pausing.
+ if (mPausingActivity != null && !didAutoPip) {
+ // Have the window manager pause its key dispatching until the new
+ // activity has started. If we're pausing the activity just because
+ // the screen is being turned off and the UI is sleeping, don't interrupt
+ // key dispatch; the same activity will pick it up again on wakeup.
+ if (!uiSleeping) {
+ prev.pauseKeyDispatchingLocked();
+ } else {
+ ProtoLog.v(WM_DEBUG_STATES, "Key dispatch not paused for screen off");
+ }
+
+ if (pauseImmediately) {
+ // If the caller said they don't want to wait for the pause, then complete
+ // the pause now.
+ completePause(false, resuming);
+ return false;
+
+ } else {
+ prev.schedulePauseTimeout();
+ return true;
+ }
+
+ } else {
+ // This activity either failed to schedule the pause or it entered PIP mode,
+ // so just treat it as being paused now.
+ ProtoLog.v(WM_DEBUG_STATES, "Activity not running or entered PiP, resuming next.");
+ if (resuming == null) {
+ mRootWindowContainer.resumeFocusedTasksTopActivities();
+ }
+ return false;
+ }
+ }
+
+ @VisibleForTesting
+ void completePause(boolean resumeNext, ActivityRecord resuming) {
+ // Complete the pausing process of a pausing activity, so it doesn't make sense to
+ // operate on non-leaf tasks.
+ // warnForNonLeafTask("completePauseLocked");
+
+ ActivityRecord prev = mPausingActivity;
+ ProtoLog.v(WM_DEBUG_STATES, "Complete pause: %s", prev);
+
+ if (prev != null) {
+ prev.setWillCloseOrEnterPip(false);
+ final boolean wasStopping = prev.isState(STOPPING);
+ prev.setState(PAUSED, "completePausedLocked");
+ if (prev.finishing) {
+ // We will update the activity visibility later, no need to do in
+ // completeFinishing(). Updating visibility here might also making the next
+ // activities to be resumed, and could result in wrong app transition due to
+ // lack of previous activity information.
+ ProtoLog.v(WM_DEBUG_STATES, "Executing finish of activity: %s", prev);
+ prev = prev.completeFinishing(false /* updateVisibility */,
+ "completePausedLocked");
+ } else if (prev.hasProcess()) {
+ ProtoLog.v(WM_DEBUG_STATES, "Enqueue pending stop if needed: %s "
+ + "wasStopping=%b visibleRequested=%b", prev, wasStopping,
+ prev.mVisibleRequested);
+ if (prev.deferRelaunchUntilPaused) {
+ // Complete the deferred relaunch that was waiting for pause to complete.
+ ProtoLog.v(WM_DEBUG_STATES, "Re-launching after pause: %s", prev);
+ prev.relaunchActivityLocked(prev.preserveWindowOnDeferredRelaunch);
+ } else if (wasStopping) {
+ // We are also stopping, the stop request must have gone soon after the pause.
+ // We can't clobber it, because the stop confirmation will not be handled.
+ // We don't need to schedule another stop, we only need to let it happen.
+ prev.setState(STOPPING, "completePausedLocked");
+ } else if (!prev.mVisibleRequested || shouldSleepOrShutDownActivities()) {
+ // Clear out any deferred client hide we might currently have.
+ prev.setDeferHidingClient(false);
+ // If we were visible then resumeTopActivities will release resources before
+ // stopping.
+ prev.addToStopping(true /* scheduleIdle */, false /* idleDelayed */,
+ "completePauseLocked");
+ }
+ } else {
+ ProtoLog.v(WM_DEBUG_STATES, "App died during pause, not stopping: %s", prev);
+ prev = null;
+ }
+ // It is possible the activity was freezing the screen before it was paused.
+ // In that case go ahead and remove the freeze this activity has on the screen
+ // since it is no longer visible.
+ if (prev != null) {
+ prev.stopFreezingScreenLocked(true /*force*/);
+ }
+ mPausingActivity = null;
+ }
+
+ if (resumeNext) {
+ final Task topRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask();
+ if (topRootTask != null && !topRootTask.shouldSleepOrShutDownActivities()) {
+ mRootWindowContainer.resumeFocusedTasksTopActivities(topRootTask, prev, null);
+ } else {
+ // checkReadyForSleep();
+ final ActivityRecord top =
+ topRootTask != null ? topRootTask.topRunningActivity() : null;
+ if (top == null || (prev != null && top != prev)) {
+ // If there are no more activities available to run, do resume anyway to start
+ // something. Also if the top activity on the root task is not the just paused
+ // activity, we need to go ahead and resume it to ensure we complete an
+ // in-flight app switch.
+ mRootWindowContainer.resumeFocusedTasksTopActivities();
+ }
+ }
+ }
+
+ if (prev != null) {
+ prev.resumeKeyDispatchingLocked();
+ }
+
+ mRootWindowContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS);
+
+ // Notify when the task stack has changed, but only if visibilities changed (not just
+ // focus). Also if there is an active root pinned task - we always want to notify it about
+ // task stack changes, because its positioning may depend on it.
+ if (mTaskSupervisor.mAppVisibilitiesChangedSinceLastPause
+ || (getDisplayArea() != null && getDisplayArea().hasPinnedTask())) {
+ mAtmService.getTaskChangeNotificationController().notifyTaskStackChanged();
+ mTaskSupervisor.mAppVisibilitiesChangedSinceLastPause = false;
+ }
+ }
+
+ @Override
+ void forAllLeafTaskFragments(Consumer<TaskFragment> callback, boolean traverseTopToBottom) {
+ final int count = mChildren.size();
+ boolean isLeafTaskFrag = true;
+ if (traverseTopToBottom) {
+ for (int i = count - 1; i >= 0; --i) {
+ final TaskFragment child = mChildren.get(i).asTaskFragment();
+ if (child != null) {
+ isLeafTaskFrag = false;
+ child.forAllLeafTaskFragments(callback, traverseTopToBottom);
+ }
+ }
+ } else {
+ for (int i = 0; i < count; i++) {
+ final TaskFragment child = mChildren.get(i).asTaskFragment();
+ if (child != null) {
+ isLeafTaskFrag = false;
+ child.forAllLeafTaskFragments(callback, traverseTopToBottom);
+ }
+ }
+ }
+ if (isLeafTaskFrag) callback.accept(this);
+ }
+
+ @Override
+ boolean forAllLeafTaskFragments(Function<TaskFragment, Boolean> callback) {
+ boolean isLeafTaskFrag = true;
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ final TaskFragment child = mChildren.get(i).asTaskFragment();
+ if (child != null) {
+ isLeafTaskFrag = false;
+ if (child.forAllLeafTaskFragments(callback)) {
+ return true;
+ }
+ }
+ }
+ if (isLeafTaskFrag) {
+ return callback.apply(this);
+ }
+ return false;
+ }
+
+ void executeAppTransition(ActivityOptions options) {
+ // No app transition applied to the task fragment.
+ }
+
+ boolean shouldSleepActivities() {
+ return false;
+ }
+
+ @Override
+ void resolveOverrideConfiguration(Configuration newParentConfig) {
+ mTmpBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds());
+ super.resolveOverrideConfiguration(newParentConfig);
+
+ int windowingMode =
+ getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
+ final int parentWindowingMode = newParentConfig.windowConfiguration.getWindowingMode();
+
+ // Resolve override windowing mode to fullscreen for home task (even on freeform
+ // display), or split-screen if in split-screen mode.
+ if (getActivityType() == ACTIVITY_TYPE_HOME && windowingMode == WINDOWING_MODE_UNDEFINED) {
+ windowingMode = WindowConfiguration.isSplitScreenWindowingMode(parentWindowingMode)
+ ? parentWindowingMode : WINDOWING_MODE_FULLSCREEN;
+ getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
+ }
+
+ // Do not allow tasks not support multi window to be in a multi-window mode, unless it is in
+ // pinned windowing mode.
+ if (!supportsMultiWindow()) {
+ final int candidateWindowingMode =
+ windowingMode != WINDOWING_MODE_UNDEFINED ? windowingMode : parentWindowingMode;
+ if (WindowConfiguration.inMultiWindowMode(candidateWindowingMode)
+ && candidateWindowingMode != WINDOWING_MODE_PINNED) {
+ getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(
+ WINDOWING_MODE_FULLSCREEN);
+ }
+ }
+
+ if (isLeafTaskFragment()) {
+ resolveLeafOnlyOverrideConfigs(newParentConfig, mTmpBounds /* previousBounds */);
+ }
+ computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig);
+ }
+
+ boolean supportsMultiWindow() {
+ return supportsMultiWindowInDisplayArea(getDisplayArea());
+ }
+
+ /**
+ * @return whether this task supports multi-window if it is in the given
+ * {@link TaskDisplayArea}.
+ */
+ boolean supportsMultiWindowInDisplayArea(@Nullable TaskDisplayArea tda) {
+ if (!mAtmService.mSupportsMultiWindow) {
+ return false;
+ }
+ final Task task = getTask();
+ if (task == null) {
+ return false;
+ }
+ if (tda == null) {
+ Slog.w(TAG, "Can't find TaskDisplayArea to determine support for multi"
+ + " window. Task id=" + getTaskId() + " attached=" + isAttached());
+ return false;
+ }
+ if (!getTask().isResizeable() && !tda.supportsNonResizableMultiWindow()) {
+ // Not support non-resizable in multi window.
+ return false;
+ }
+
+ return tda.supportsActivityMinWidthHeightMultiWindow(mMinWidth, mMinHeight);
+ }
+
+ private int getTaskId() {
+ return getTask().mTaskId;
+ }
+
+ private void resolveLeafOnlyOverrideConfigs(Configuration newParentConfig,
+ Rect previousBounds) {
+
+ int windowingMode =
+ getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
+ if (windowingMode == WINDOWING_MODE_UNDEFINED) {
+ windowingMode = newParentConfig.windowConfiguration.getWindowingMode();
+ }
+ // Commit the resolved windowing mode so the canSpecifyOrientation won't get the old
+ // mode that may cause the bounds to be miscalculated, e.g. letterboxed.
+ getConfiguration().windowConfiguration.setWindowingMode(windowingMode);
+ Rect outOverrideBounds = getResolvedOverrideConfiguration().windowConfiguration.getBounds();
+
+ if (windowingMode == WINDOWING_MODE_FULLSCREEN) {
+ // Use empty bounds to indicate "fill parent".
+ outOverrideBounds.setEmpty();
+ // The bounds for fullscreen mode shouldn't be adjusted by minimal size. Otherwise if
+ // the parent or display is smaller than the size, the content may be cropped.
+ return;
+ }
+
+ adjustForMinimalTaskDimensions(outOverrideBounds, previousBounds, newParentConfig);
+ if (windowingMode == WINDOWING_MODE_FREEFORM) {
+ computeFreeformBounds(outOverrideBounds, newParentConfig);
+ return;
+ }
+ }
+
+ /** Computes bounds for {@link WindowConfiguration#WINDOWING_MODE_FREEFORM}. */
+ private void computeFreeformBounds(@NonNull Rect outBounds,
+ @NonNull Configuration newParentConfig) {
+ // by policy, make sure the window remains within parent somewhere
+ final float density =
+ ((float) newParentConfig.densityDpi) / DisplayMetrics.DENSITY_DEFAULT;
+ final Rect parentBounds =
+ new Rect(newParentConfig.windowConfiguration.getBounds());
+ final DisplayContent display = getDisplayContent();
+ if (display != null) {
+ // If a freeform window moves below system bar, there is no way to move it again
+ // by touch. Because its caption is covered by system bar. So we exclude them
+ // from root task bounds. and then caption will be shown inside stable area.
+ final Rect stableBounds = new Rect();
+ display.getStableRect(stableBounds);
+ parentBounds.intersect(stableBounds);
+ }
+
+ fitWithinBounds(outBounds, parentBounds,
+ (int) (density * WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP),
+ (int) (density * WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP));
+
+ // Prevent to overlap caption with stable insets.
+ final int offsetTop = parentBounds.top - outBounds.top;
+ if (offsetTop > 0) {
+ outBounds.offset(0, offsetTop);
+ }
+ }
+
+ /**
+ * Adjusts bounds to stay within root task bounds.
+ *
+ * Since bounds might be outside of root task bounds, this method tries to move the bounds in
+ * a way that keep them unchanged, but be contained within the root task bounds.
+ *
+ * @param bounds Bounds to be adjusted.
+ * @param rootTaskBounds Bounds within which the other bounds should remain.
+ * @param overlapPxX The amount of px required to be visible in the X dimension.
+ * @param overlapPxY The amount of px required to be visible in the Y dimension.
+ */
+ private static void fitWithinBounds(Rect bounds, Rect rootTaskBounds, int overlapPxX,
+ int overlapPxY) {
+ if (rootTaskBounds == null || rootTaskBounds.isEmpty() || rootTaskBounds.contains(bounds)) {
+ return;
+ }
+
+ // For each side of the parent (eg. left), check if the opposing side of the window (eg.
+ // right) is at least overlap pixels away. If less, offset the window by that difference.
+ int horizontalDiff = 0;
+ // If window is smaller than overlap, use it's smallest dimension instead
+ int overlapLR = Math.min(overlapPxX, bounds.width());
+ if (bounds.right < (rootTaskBounds.left + overlapLR)) {
+ horizontalDiff = overlapLR - (bounds.right - rootTaskBounds.left);
+ } else if (bounds.left > (rootTaskBounds.right - overlapLR)) {
+ horizontalDiff = -(overlapLR - (rootTaskBounds.right - bounds.left));
+ }
+ int verticalDiff = 0;
+ int overlapTB = Math.min(overlapPxY, bounds.width());
+ if (bounds.bottom < (rootTaskBounds.top + overlapTB)) {
+ verticalDiff = overlapTB - (bounds.bottom - rootTaskBounds.top);
+ } else if (bounds.top > (rootTaskBounds.bottom - overlapTB)) {
+ verticalDiff = -(overlapTB - (rootTaskBounds.bottom - bounds.top));
+ }
+ bounds.offset(horizontalDiff, verticalDiff);
+ }
+
+ /**
+ * Ensures all visible activities at or below the input activity have the right configuration.
+ */
+ void ensureVisibleActivitiesConfiguration(ActivityRecord start, boolean preserveWindow) {
+ mEnsureVisibleActivitiesConfigHelper.process(start, preserveWindow);
+ }
+
+ void adjustForMinimalTaskDimensions(@NonNull Rect bounds, @NonNull Rect previousBounds,
+ @NonNull Configuration parentConfig) {
+ int minWidth = mMinWidth;
+ int minHeight = mMinHeight;
+ // If the task has no requested minimal size, we'd like to enforce a minimal size
+ // so that the user can not render the task fragment too small to manipulate. We don't need
+ // to do this for the root pinned task as the bounds are controlled by the system.
+ if (!inPinnedWindowingMode()) {
+ final int defaultMinSizeDp = mRootWindowContainer.mDefaultMinSizeOfResizeableTaskDp;
+ final float density = (float) parentConfig.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
+ final int defaultMinSize = (int) (defaultMinSizeDp * density);
+
+ if (minWidth == INVALID_MIN_SIZE) {
+ minWidth = defaultMinSize;
+ }
+ if (minHeight == INVALID_MIN_SIZE) {
+ minHeight = defaultMinSize;
+ }
+ }
+ if (bounds.isEmpty()) {
+ // If inheriting parent bounds, check if parent bounds adhere to minimum size. If they
+ // do, we can just skip.
+ final Rect parentBounds = parentConfig.windowConfiguration.getBounds();
+ if (parentBounds.width() >= minWidth && parentBounds.height() >= minHeight) {
+ return;
+ }
+ bounds.set(parentBounds);
+ }
+ final boolean adjustWidth = minWidth > bounds.width();
+ final boolean adjustHeight = minHeight > bounds.height();
+ if (!(adjustWidth || adjustHeight)) {
+ return;
+ }
+
+ if (adjustWidth) {
+ if (!previousBounds.isEmpty() && bounds.right == previousBounds.right) {
+ bounds.left = bounds.right - minWidth;
+ } else {
+ // Either left bounds match, or neither match, or the previous bounds were
+ // fullscreen and we default to keeping left.
+ bounds.right = bounds.left + minWidth;
+ }
+ }
+ if (adjustHeight) {
+ if (!previousBounds.isEmpty() && bounds.bottom == previousBounds.bottom) {
+ bounds.top = bounds.bottom - minHeight;
+ } else {
+ // Either top bounds match, or neither match, or the previous bounds were
+ // fullscreen and we default to keeping top.
+ bounds.bottom = bounds.top + minHeight;
+ }
+ }
+ }
+
+ void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
+ @NonNull Configuration parentConfig) {
+ computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */,
+ null /* compatInsets */);
+ }
+
+ void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
+ @NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo) {
+ if (overrideDisplayInfo != null) {
+ // Make sure the screen related configs can be computed by the provided display info.
+ inOutConfig.screenLayout = Configuration.SCREENLAYOUT_UNDEFINED;
+ invalidateAppBoundsConfig(inOutConfig);
+ }
+ computeConfigResourceOverrides(inOutConfig, parentConfig, overrideDisplayInfo,
+ null /* compatInsets */);
+ }
+
+ void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
+ @NonNull Configuration parentConfig,
+ @Nullable ActivityRecord.CompatDisplayInsets compatInsets) {
+ if (compatInsets != null) {
+ // Make sure the app bounds can be computed by the compat insets.
+ invalidateAppBoundsConfig(inOutConfig);
+ }
+ computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */,
+ compatInsets);
+ }
+
+ /**
+ * Forces the app bounds related configuration can be computed by
+ * {@link #computeConfigResourceOverrides(Configuration, Configuration, DisplayInfo,
+ * ActivityRecord.CompatDisplayInsets)}.
+ */
+ private static void invalidateAppBoundsConfig(@NonNull Configuration inOutConfig) {
+ final Rect appBounds = inOutConfig.windowConfiguration.getAppBounds();
+ if (appBounds != null) {
+ appBounds.setEmpty();
+ }
+ inOutConfig.screenWidthDp = Configuration.SCREEN_WIDTH_DP_UNDEFINED;
+ inOutConfig.screenHeightDp = Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
+ }
+
+ /**
+ * Calculates configuration values used by the client to get resources. This should be run
+ * using app-facing bounds (bounds unmodified by animations or transient interactions).
+ *
+ * This assumes bounds are non-empty/null. For the null-bounds case, the caller is likely
+ * configuring an "inherit-bounds" window which means that all configuration settings would
+ * just be inherited from the parent configuration.
+ **/
+ void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
+ @NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo,
+ @Nullable ActivityRecord.CompatDisplayInsets compatInsets) {
+ int windowingMode = inOutConfig.windowConfiguration.getWindowingMode();
+ if (windowingMode == WINDOWING_MODE_UNDEFINED) {
+ windowingMode = parentConfig.windowConfiguration.getWindowingMode();
+ }
+
+ float density = inOutConfig.densityDpi;
+ if (density == Configuration.DENSITY_DPI_UNDEFINED) {
+ density = parentConfig.densityDpi;
+ }
+ density *= DisplayMetrics.DENSITY_DEFAULT_SCALE;
+
+ // The bounds may have been overridden at this level. If the parent cannot cover these
+ // bounds, the configuration is still computed according to the override bounds.
+ final boolean insideParentBounds;
+
+ final Rect parentBounds = parentConfig.windowConfiguration.getBounds();
+ final Rect resolvedBounds = inOutConfig.windowConfiguration.getBounds();
+ if (resolvedBounds == null || resolvedBounds.isEmpty()) {
+ mTmpFullBounds.set(parentBounds);
+ insideParentBounds = true;
+ } else {
+ mTmpFullBounds.set(resolvedBounds);
+ insideParentBounds = parentBounds.contains(resolvedBounds);
+ }
+
+ // Non-null compatibility insets means the activity prefers to keep its original size, so
+ // out bounds doesn't need to be restricted by the parent or current display
+ final boolean customContainerPolicy = compatInsets != null;
+
+ Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
+ if (outAppBounds == null || outAppBounds.isEmpty()) {
+ // App-bounds hasn't been overridden, so calculate a value for it.
+ inOutConfig.windowConfiguration.setAppBounds(mTmpFullBounds);
+ outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
+
+ if (!customContainerPolicy && windowingMode != WINDOWING_MODE_FREEFORM) {
+ final Rect containingAppBounds;
+ if (insideParentBounds) {
+ containingAppBounds = parentConfig.windowConfiguration.getAppBounds();
+ } else {
+ // Restrict appBounds to display non-decor rather than parent because the
+ // override bounds are beyond the parent. Otherwise, it won't match the
+ // overridden bounds.
+ final TaskDisplayArea displayArea = getDisplayArea();
+ containingAppBounds = displayArea != null
+ ? displayArea.getWindowConfiguration().getAppBounds() : null;
+ }
+ if (containingAppBounds != null && !containingAppBounds.isEmpty()) {
+ outAppBounds.intersect(containingAppBounds);
+ }
+ }
+ }
+
+ if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED
+ || inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
+ if (!customContainerPolicy && WindowConfiguration.isFloating(windowingMode)) {
+ mTmpNonDecorBounds.set(mTmpFullBounds);
+ mTmpStableBounds.set(mTmpFullBounds);
+ } else if (!customContainerPolicy
+ && (overrideDisplayInfo != null || getDisplayContent() != null)) {
+ final DisplayInfo di = overrideDisplayInfo != null
+ ? overrideDisplayInfo
+ : getDisplayContent().getDisplayInfo();
+
+ // For calculating screenWidthDp, screenWidthDp, we use the stable inset screen
+ // area, i.e. the screen area without the system bars.
+ // The non decor inset are areas that could never be removed in Honeycomb. See
+ // {@link WindowManagerPolicy#getNonDecorInsetsLw}.
+ calculateInsetFrames(mTmpNonDecorBounds, mTmpStableBounds, mTmpFullBounds, di);
+ } else {
+ // Apply the given non-decor and stable insets to calculate the corresponding bounds
+ // for screen size of configuration.
+ int rotation = inOutConfig.windowConfiguration.getRotation();
+ if (rotation == ROTATION_UNDEFINED) {
+ rotation = parentConfig.windowConfiguration.getRotation();
+ }
+ if (rotation != ROTATION_UNDEFINED && customContainerPolicy) {
+ mTmpNonDecorBounds.set(mTmpFullBounds);
+ mTmpStableBounds.set(mTmpFullBounds);
+ compatInsets.getBoundsByRotation(mTmpBounds, rotation);
+ intersectWithInsetsIfFits(mTmpNonDecorBounds, mTmpBounds,
+ compatInsets.mNonDecorInsets[rotation]);
+ intersectWithInsetsIfFits(mTmpStableBounds, mTmpBounds,
+ compatInsets.mStableInsets[rotation]);
+ outAppBounds.set(mTmpNonDecorBounds);
+ } else {
+ // Set to app bounds because it excludes decor insets.
+ mTmpNonDecorBounds.set(outAppBounds);
+ mTmpStableBounds.set(outAppBounds);
+ }
+ }
+
+ if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
+ final int overrideScreenWidthDp = (int) (mTmpStableBounds.width() / density);
+ inOutConfig.screenWidthDp = (insideParentBounds && !customContainerPolicy)
+ ? Math.min(overrideScreenWidthDp, parentConfig.screenWidthDp)
+ : overrideScreenWidthDp;
+ }
+ if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
+ final int overrideScreenHeightDp = (int) (mTmpStableBounds.height() / density);
+ inOutConfig.screenHeightDp = (insideParentBounds && !customContainerPolicy)
+ ? Math.min(overrideScreenHeightDp, parentConfig.screenHeightDp)
+ : overrideScreenHeightDp;
+ }
+
+ if (inOutConfig.smallestScreenWidthDp
+ == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
+ if (WindowConfiguration.isFloating(windowingMode)) {
+ // For floating tasks, calculate the smallest width from the bounds of the task
+ inOutConfig.smallestScreenWidthDp = (int) (
+ Math.min(mTmpFullBounds.width(), mTmpFullBounds.height()) / density);
+ }
+ // otherwise, it will just inherit
+ }
+ }
+
+ if (inOutConfig.orientation == ORIENTATION_UNDEFINED) {
+ inOutConfig.orientation = (inOutConfig.screenWidthDp <= inOutConfig.screenHeightDp)
+ ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
+ }
+ if (inOutConfig.screenLayout == Configuration.SCREENLAYOUT_UNDEFINED) {
+ // For calculating screen layout, we need to use the non-decor inset screen area for the
+ // calculation for compatibility reasons, i.e. screen area without system bars that
+ // could never go away in Honeycomb.
+ int compatScreenWidthDp = (int) (mTmpNonDecorBounds.width() / density);
+ int compatScreenHeightDp = (int) (mTmpNonDecorBounds.height() / density);
+ // Use overrides if provided. If both overrides are provided, mTmpNonDecorBounds is
+ // undefined so it can't be used.
+ if (inOutConfig.screenWidthDp != Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
+ compatScreenWidthDp = inOutConfig.screenWidthDp;
+ }
+ if (inOutConfig.screenHeightDp != Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
+ compatScreenHeightDp = inOutConfig.screenHeightDp;
+ }
+ // Reducing the screen layout starting from its parent config.
+ inOutConfig.screenLayout = computeScreenLayoutOverride(parentConfig.screenLayout,
+ compatScreenWidthDp, compatScreenHeightDp);
+ }
+ }
+
+ /**
+ * Gets bounds with non-decor and stable insets applied respectively.
+ *
+ * If bounds overhangs the display, those edges will not get insets. See
+ * {@link #intersectWithInsetsIfFits}
+ *
+ * @param outNonDecorBounds where to place bounds with non-decor insets applied.
+ * @param outStableBounds where to place bounds with stable insets applied.
+ * @param bounds the bounds to inset.
+ */
+ void calculateInsetFrames(Rect outNonDecorBounds, Rect outStableBounds, Rect bounds,
+ DisplayInfo displayInfo) {
+ outNonDecorBounds.set(bounds);
+ outStableBounds.set(bounds);
+ final Task rootTask = getRootTaskFragment().asTask();
+ if (rootTask == null || rootTask.mDisplayContent == null) {
+ return;
+ }
+ mTmpBounds.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
+
+ final DisplayPolicy policy = rootTask.mDisplayContent.getDisplayPolicy();
+ policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth,
+ displayInfo.logicalHeight, displayInfo.displayCutout, mTmpInsets);
+ intersectWithInsetsIfFits(outNonDecorBounds, mTmpBounds, mTmpInsets);
+
+ policy.convertNonDecorInsetsToStableInsets(mTmpInsets, displayInfo.rotation);
+ intersectWithInsetsIfFits(outStableBounds, mTmpBounds, mTmpInsets);
+ }
+
+ /**
+ * Intersects inOutBounds with intersectBounds-intersectInsets. If inOutBounds is larger than
+ * intersectBounds on a side, then the respective side will not be intersected.
+ *
+ * The assumption is that if inOutBounds is initially larger than intersectBounds, then the
+ * inset on that side is no-longer applicable. This scenario happens when a task's minimal
+ * bounds are larger than the provided parent/display bounds.
+ *
+ * @param inOutBounds the bounds to intersect.
+ * @param intersectBounds the bounds to intersect with.
+ * @param intersectInsets insets to apply to intersectBounds before intersecting.
+ */
+ static void intersectWithInsetsIfFits(
+ Rect inOutBounds, Rect intersectBounds, Rect intersectInsets) {
+ if (inOutBounds.right <= intersectBounds.right) {
+ inOutBounds.right =
+ Math.min(intersectBounds.right - intersectInsets.right, inOutBounds.right);
+ }
+ if (inOutBounds.bottom <= intersectBounds.bottom) {
+ inOutBounds.bottom =
+ Math.min(intersectBounds.bottom - intersectInsets.bottom, inOutBounds.bottom);
+ }
+ if (inOutBounds.left >= intersectBounds.left) {
+ inOutBounds.left =
+ Math.max(intersectBounds.left + intersectInsets.left, inOutBounds.left);
+ }
+ if (inOutBounds.top >= intersectBounds.top) {
+ inOutBounds.top =
+ Math.max(intersectBounds.top + intersectInsets.top, inOutBounds.top);
+ }
+ }
+
+ /** Computes LONG, SIZE and COMPAT parts of {@link Configuration#screenLayout}. */
+ static int computeScreenLayoutOverride(int sourceScreenLayout, int screenWidthDp,
+ int screenHeightDp) {
+ sourceScreenLayout = sourceScreenLayout
+ & (Configuration.SCREENLAYOUT_LONG_MASK | Configuration.SCREENLAYOUT_SIZE_MASK);
+ final int longSize = Math.max(screenWidthDp, screenHeightDp);
+ final int shortSize = Math.min(screenWidthDp, screenHeightDp);
+ return Configuration.reduceScreenLayout(sourceScreenLayout, longSize, shortSize);
+ }
+
+ @Override
+ public int getActivityType() {
+ final int applicationType = super.getActivityType();
+ if (applicationType != ACTIVITY_TYPE_UNDEFINED || !hasChild()) {
+ return applicationType;
+ }
+ return getTopChild().getActivityType();
+ }
+
+ boolean dump(String prefix, FileDescriptor fd, PrintWriter pw, boolean dumpAll,
+ boolean dumpClient, String dumpPackage, final boolean needSep, Runnable header) {
+ boolean printed = false;
+ Runnable headerPrinter = () -> {
+ if (needSep) {
+ pw.println();
+ }
+ if (header != null) {
+ header.run();
+ }
+
+ dumpInner(prefix, pw, dumpAll, dumpPackage);
+ };
+
+ if (dumpPackage == null) {
+ // If we are not filtering by package, we want to print absolutely everything,
+ // so always print the header even if there are no tasks/activities inside.
+ headerPrinter.run();
+ headerPrinter = null;
+ printed = true;
+ }
+
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ WindowContainer child = mChildren.get(i);
+ if (child.asTaskFragment() != null) {
+ printed |= child.asTaskFragment().dump(prefix + " ", fd, pw, dumpAll,
+ dumpClient, dumpPackage, needSep, headerPrinter);
+ } else if (child.asActivityRecord() != null) {
+ ActivityRecord.dumpActivity(fd, pw, i, child.asActivityRecord(), prefix + " ",
+ "Hist ", true, !dumpAll, dumpClient, dumpPackage, false, headerPrinter,
+ getTask());
+ }
+ }
+
+ return printed;
+ }
+
+ void dumpInner(String prefix, PrintWriter pw, boolean dumpAll, String dumpPackage) {
+ pw.print(prefix); pw.print("* "); pw.println(this);
+ pw.println(prefix + " mBounds=" + getRequestedOverrideBounds());
+ }
+}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 0cd09807..de3af2c 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -16,11 +16,13 @@
package com.android.server.wm;
-
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
@@ -124,9 +126,16 @@
/** The final animation targets derived from participants after promotion. */
private ArraySet<WindowContainer> mTargets = null;
+ private TransitionInfo.AnimationOptions mOverrideOptions;
+
private @TransitionState int mState = STATE_COLLECTING;
private boolean mReadyCalled = false;
+ // TODO(b/188595497): remove when not needed.
+ /** @see RecentsAnimationController#mNavigationBarAttachedToApp */
+ private boolean mNavBarAttachedToApp = false;
+ private int mNavBarDisplayId = INVALID_DISPLAY;
+
Transition(@WindowManager.TransitionType int type, @WindowManager.TransitionFlags int flags,
TransitionController controller, BLASTSyncEngine syncEngine) {
mType = type;
@@ -136,6 +145,10 @@
mSyncId = mSyncEngine.startSyncSet(this);
}
+ void addFlag(int flag) {
+ mFlags |= flag;
+ }
+
@VisibleForTesting
int getSyncId() {
return mSyncId;
@@ -207,6 +220,15 @@
}
/**
+ * Set animation options for collecting transition by ActivityRecord.
+ * @param options AnimationOptions captured from ActivityOptions
+ */
+ void setOverrideAnimation(TransitionInfo.AnimationOptions options) {
+ if (mSyncId < 0) return;
+ mOverrideOptions = options;
+ }
+
+ /**
* Call this when all known changes related to this transition have been applied. Until
* all participants have finished drawing, the transition can still collect participants.
*
@@ -282,6 +304,9 @@
" Commit activity becoming invisible: %s", ar);
ar.commitVisibility(false /* visible */, false /* performLayout */);
}
+ if (ar != null) {
+ mController.dispatchLegacyAppTransitionFinished(ar);
+ }
final WallpaperWindowToken wt = mParticipants.valueAt(i).asWallpaperToken();
if (wt != null && !wt.isVisibleRequested()) {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
@@ -289,6 +314,8 @@
wt.commitVisibility(false /* visible */);
}
}
+
+ legacyRestoreNavigationBarFromApp();
}
void abort() {
@@ -297,6 +324,7 @@
if (mState != STATE_COLLECTING) {
throw new IllegalStateException("Too late to abort.");
}
+ mController.dispatchLegacyAppTransitionCancelled();
mState = STATE_ABORT;
// Syncengine abort will call through to onTransactionReady()
mSyncEngine.abort(mSyncId);
@@ -327,6 +355,7 @@
mController.mAtm.mRootWindowContainer.getDisplayContent(displayId)
.getPendingTransaction().merge(transaction);
mSyncId = -1;
+ mOverrideOptions = null;
return;
}
@@ -340,6 +369,10 @@
// Resolve the animating targets from the participants
mTargets = calculateTargets(mParticipants, mChanges);
final TransitionInfo info = calculateTransitionInfo(mType, mFlags, mTargets, mChanges);
+ info.setAnimationOptions(mOverrideOptions);
+
+ // TODO(b/188669821): Move to animation impl in shell.
+ handleLegacyRecentsStartBehavior(displayId, info);
handleNonAppWindowsInTransition(displayId, mType, mFlags);
@@ -360,6 +393,7 @@
mFinishTransaction = mController.mAtm.mWindowManager.mTransactionFactory.get();
buildFinishTransaction(mFinishTransaction, info.getRootLeash());
if (mController.getTransitionPlayer() != null) {
+ mController.dispatchLegacyAppTransitionStarting(info);
try {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
"Calling onTransitionReady: %s", info);
@@ -375,6 +409,7 @@
cleanUpOnFailure();
}
mSyncId = -1;
+ mOverrideOptions = null;
}
/**
@@ -394,6 +429,100 @@
finishTransition();
}
+ /** @see RecentsAnimationController#attachNavigationBarToApp */
+ private void handleLegacyRecentsStartBehavior(int displayId, TransitionInfo info) {
+ if ((mFlags & TRANSIT_FLAG_IS_RECENTS) == 0) {
+ return;
+ }
+ final DisplayContent dc =
+ mController.mAtm.mRootWindowContainer.getDisplayContent(displayId);
+ if (dc == null || !dc.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition()
+ // Skip the case where the nav bar is controlled by fade rotation.
+ || dc.getFadeRotationAnimationController() != null) {
+ return;
+ }
+
+ WindowContainer topWC = null;
+ // Find the top-most non-home, closing app.
+ for (int i = 0; i < info.getChanges().size(); ++i) {
+ final TransitionInfo.Change c = info.getChanges().get(i);
+ if (c.getTaskInfo() == null || c.getTaskInfo().displayId != displayId
+ || c.getTaskInfo().getActivityType() != ACTIVITY_TYPE_STANDARD
+ || !(c.getMode() == TRANSIT_CLOSE || c.getMode() == TRANSIT_TO_BACK)) {
+ continue;
+ }
+ topWC = WindowContainer.fromBinder(c.getContainer().asBinder());
+ break;
+ }
+ if (topWC == null || topWC.inMultiWindowMode()) {
+ return;
+ }
+
+ final WindowState navWindow = dc.getDisplayPolicy().getNavigationBar();
+ if (navWindow == null || navWindow.mToken == null) {
+ return;
+ }
+ mNavBarAttachedToApp = true;
+ mNavBarDisplayId = displayId;
+ navWindow.mToken.cancelAnimation();
+ final SurfaceControl.Transaction t = navWindow.mToken.getPendingTransaction();
+ final SurfaceControl navSurfaceControl = navWindow.mToken.getSurfaceControl();
+ t.reparent(navSurfaceControl, topWC.getSurfaceControl());
+ t.show(navSurfaceControl);
+
+ final WindowContainer imeContainer = dc.getImeContainer();
+ if (imeContainer.isVisible()) {
+ t.setRelativeLayer(navSurfaceControl, imeContainer.getSurfaceControl(), 1);
+ } else {
+ // Place the nav bar on top of anything else in the top activity.
+ t.setLayer(navSurfaceControl, Integer.MAX_VALUE);
+ }
+ if (mController.mStatusBar != null) {
+ mController.mStatusBar.setNavigationBarLumaSamplingEnabled(displayId, false);
+ }
+ }
+
+ /** @see RecentsAnimationController#restoreNavigationBarFromApp */
+ void legacyRestoreNavigationBarFromApp() {
+ if (!mNavBarAttachedToApp) return;
+ mNavBarAttachedToApp = false;
+
+ if (mController.mStatusBar != null) {
+ mController.mStatusBar.setNavigationBarLumaSamplingEnabled(mNavBarDisplayId, true);
+ }
+
+ final DisplayContent dc =
+ mController.mAtm.mRootWindowContainer.getDisplayContent(mNavBarDisplayId);
+ final WindowState navWindow = dc.getDisplayPolicy().getNavigationBar();
+ if (navWindow == null) return;
+ navWindow.setSurfaceTranslationY(0);
+
+ final WindowToken navToken = navWindow.mToken;
+ if (navToken == null) return;
+ final SurfaceControl.Transaction t = dc.getPendingTransaction();
+ final WindowContainer parent = navToken.getParent();
+ t.setLayer(navToken.getSurfaceControl(), navToken.getLastLayer());
+
+ boolean animate = false;
+ // Search for the home task. If it is supposed to be visible, then the navbar is not at
+ // the bottom of the screen, so we need to animate it.
+ for (int i = 0; i < mTargets.size(); ++i) {
+ final Task task = mTargets.valueAt(i).asTask();
+ if (task == null || !task.isHomeOrRecentsRootTask()) continue;
+ animate = task.isVisibleRequested();
+ break;
+ }
+
+ if (animate) {
+ final NavBarFadeAnimationController controller =
+ new NavBarFadeAnimationController(dc);
+ controller.fadeWindowToken(true);
+ } else {
+ // Reparent the SurfaceControl of nav bar token back.
+ t.reparent(navToken.getSurfaceControl(), parent.getSurfaceControl());
+ }
+ }
+
private void handleNonAppWindowsInTransition(int displayId,
@WindowManager.TransitionType int transit, int flags) {
final DisplayContent dc =
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index cc63c49..9195f4d 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -17,6 +17,12 @@
package com.android.server.wm;
import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_OPEN;
import android.annotation.NonNull;
@@ -24,14 +30,18 @@
import android.app.ActivityManager;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.util.Slog;
import android.view.WindowManager;
import android.window.IRemoteTransition;
import android.window.ITransitionPlayer;
+import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
import com.android.internal.protolog.ProtoLogGroup;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.server.LocalServices;
+import com.android.server.statusbar.StatusBarManagerInternal;
import java.util.ArrayList;
@@ -44,6 +54,9 @@
private ITransitionPlayer mTransitionPlayer;
final ActivityTaskManagerService mAtm;
+ private final ArrayList<WindowManagerInternal.AppTransitionListener> mLegacyListeners =
+ new ArrayList<>();
+
/**
* Currently playing transitions (in the order they were started). When finished, records are
* removed from this list.
@@ -62,8 +75,12 @@
/** The transition currently being constructed (collecting participants). */
private Transition mCollectingTransition = null;
+ // TODO(b/188595497): remove when not needed.
+ final StatusBarManagerInternal mStatusBar;
+
TransitionController(ActivityTaskManagerService atm) {
mAtm = atm;
+ mStatusBar = LocalServices.getService(StatusBarManagerInternal.class);
}
/** @see #createTransition(int, int) */
@@ -87,6 +104,7 @@
mCollectingTransition = new Transition(type, flags, this, mAtm.mWindowManager.mSyncEngine);
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Creating Transition: %s",
mCollectingTransition);
+ dispatchLegacyAppTransitionPending();
return mCollectingTransition;
}
@@ -240,6 +258,12 @@
mCollectingTransition.collectExistenceChange(wc);
}
+ /** @see Transition#setOverrideAnimation */
+ void setOverrideAnimation(TransitionInfo.AnimationOptions options) {
+ if (mCollectingTransition == null) return;
+ mCollectingTransition.setOverrideAnimation(options);
+ }
+
/** @see Transition#setReady */
void setReady(boolean ready) {
if (mCollectingTransition == null) return;
@@ -279,4 +303,58 @@
mCollectingTransition = null;
}
+ /**
+ * Explicitly mark the collectingTransition as being part of recents gesture. Used for legacy
+ * behaviors.
+ * TODO(b/188669821): Remove once legacy recents behavior is moved to shell.
+ */
+ void setIsLegacyRecents() {
+ if (mCollectingTransition == null) return;
+ mCollectingTransition.addFlag(TRANSIT_FLAG_IS_RECENTS);
+ }
+
+ void legacyDetachNavigationBarFromApp(@NonNull IBinder token) {
+ final Transition transition = Transition.fromBinder(token);
+ if (transition == null || !mPlayingTransitions.contains(transition)) {
+ Slog.e(TAG, "Transition isn't playing: " + token);
+ return;
+ }
+ transition.legacyRestoreNavigationBarFromApp();
+ }
+
+ void registerLegacyListener(WindowManagerInternal.AppTransitionListener listener) {
+ mLegacyListeners.add(listener);
+ }
+
+ void dispatchLegacyAppTransitionPending() {
+ for (int i = 0; i < mLegacyListeners.size(); ++i) {
+ mLegacyListeners.get(i).onAppTransitionPendingLocked();
+ }
+ }
+
+ void dispatchLegacyAppTransitionStarting(TransitionInfo info) {
+ final boolean keyguardGoingAway = info.getType() == TRANSIT_KEYGUARD_GOING_AWAY
+ || (info.getFlags() & (TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE
+ | TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION
+ | TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER
+ | TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION)) != 0;
+ for (int i = 0; i < mLegacyListeners.size(); ++i) {
+ mLegacyListeners.get(i).onAppTransitionStartingLocked(keyguardGoingAway,
+ 0 /* durationHint */, SystemClock.uptimeMillis(),
+ AnimationAdapter.STATUS_BAR_TRANSITION_DURATION);
+ }
+ }
+
+ void dispatchLegacyAppTransitionFinished(ActivityRecord ar) {
+ for (int i = 0; i < mLegacyListeners.size(); ++i) {
+ mLegacyListeners.get(i).onAppTransitionFinishedLocked(ar.token);
+ }
+ }
+
+ void dispatchLegacyAppTransitionCancelled() {
+ for (int i = 0; i < mLegacyListeners.size(); ++i) {
+ mLegacyListeners.get(i).onAppTransitionCancelledLocked(
+ false /* keyguardGoingAway */);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index b1c7e19..7b8cfc4 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1671,6 +1671,15 @@
return false;
}
+ boolean forAllLeafTaskFragments(Function<TaskFragment, Boolean> callback) {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ if (mChildren.get(i).forAllLeafTaskFragments(callback)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* For all root tasks at or below this container call the callback.
*
@@ -1739,6 +1748,19 @@
}
}
+ void forAllLeafTaskFragments(Consumer<TaskFragment> callback, boolean traverseTopToBottom) {
+ final int count = mChildren.size();
+ if (traverseTopToBottom) {
+ for (int i = count - 1; i >= 0; --i) {
+ mChildren.get(i).forAllLeafTaskFragments(callback, traverseTopToBottom);
+ }
+ } else {
+ for (int i = 0; i < count; i++) {
+ mChildren.get(i).forAllLeafTaskFragments(callback, traverseTopToBottom);
+ }
+ }
+ }
+
/**
* For all root tasks at or below this container call the callback.
*
@@ -3075,6 +3097,11 @@
}
/** Cheap way of doing cast and instanceof. */
+ TaskFragment asTaskFragment() {
+ return null;
+ }
+
+ /** Cheap way of doing cast and instanceof. */
WindowToken asWindowToken() {
return null;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 47087cf..f2a926c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -40,6 +40,7 @@
import com.android.server.policy.WindowManagerPolicy;
import java.util.List;
+import java.util.Set;
/**
* Window manager local system service interface.
@@ -54,17 +55,18 @@
*/
public interface AccessibilityControllerInternal {
/**
- * Enable the accessibility trace logging.
+ * Start tracing for the given logging types.
+ * @param loggingTypeFlags flags of the logging types enabled.
*/
- void startTrace();
+ void startTrace(long loggingTypeFlags);
/**
- * Disable the accessibility trace logging.
+ * Disable accessibility tracing for all logging types.
*/
void stopTrace();
/**
- * Is trace enabled or not.
+ * Is tracing enabled for any logging type.
*/
boolean isAccessibilityTracingEnabled();
@@ -73,20 +75,23 @@
*
* @param where A string to identify this log entry, which can be used to filter/search
* through the tracing file.
+ * @param loggingTypeFlags The flags for the logging types this log entry belongs to.
* @param callingParams The parameters for the method to be logged.
* @param a11yDump The proto byte array for a11y state when the entry is generated.
* @param callingUid The calling uid.
* @param stackTrace The stack trace, null if not needed.
+ * @param ignoreStackEntries The stack entries can be removed
*/
void logTrace(
- String where, String callingParams, byte[] a11yDump, int callingUid,
- StackTraceElement[] stackTrace);
+ String where, long loggingTypeFlags, String callingParams, byte[] a11yDump,
+ int callingUid, StackTraceElement[] stackTrace, Set<String> ignoreStackEntries);
/**
* Add an accessibility trace entry.
*
* @param where A string to identify this log entry, which can be used to filter/search
* through the tracing file.
+ * @param loggingTypeFlags The flags for the logging types this log entry belongs to.
* @param callingParams The parameters for the method to be logged.
* @param a11yDump The proto byte array for a11y state when the entry is generated.
* @param callingUid The calling uid.
@@ -94,9 +99,11 @@
* @param timeStamp The time when the method to be logged is called.
* @param processId The calling process Id.
* @param threadId The calling thread Id.
+ * @param ignoreStackEntries The stack entries can be removed
*/
- void logTrace(String where, String callingParams, byte[] a11yDump, int callingUid,
- StackTraceElement[] callStack, long timeStamp, int processId, long threadId);
+ void logTrace(String where, long loggingTypeFlags, String callingParams,
+ byte[] a11yDump, int callingUid, StackTraceElement[] callStack, long timeStamp,
+ int processId, long threadId, Set<String> ignoreStackEntries);
}
/**
@@ -115,6 +122,16 @@
*/
void onWindowsForAccessibilityChanged(boolean forceSend, int topFocusedDisplayId,
IBinder topFocusedWindowToken, @NonNull List<WindowInfo> windows);
+
+ /**
+ * Called when the display is reparented and becomes an embedded
+ * display. The {@link WindowsForAccessibilityCallback} with the given embedded
+ * display will be replaced by the {@link WindowsForAccessibilityCallback}
+ * associated with its parent display at the same time.
+ *
+ * @param embeddedDisplayId The embedded display Id.
+ */
+ void onDisplayReparented(int embeddedDisplayId);
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 6d51849..5fabaab 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7591,6 +7591,7 @@
public void registerAppTransitionListener(AppTransitionListener listener) {
synchronized (mGlobalLock) {
getDefaultDisplayContentLocked().mAppTransition.registerListenerLocked(listener);
+ mAtmService.getTransitionController().registerLegacyListener(listener);
}
}
@@ -7860,7 +7861,10 @@
@Override
public boolean isTouchOrFaketouchDevice() {
synchronized (mGlobalLock) {
- // All touchable devices are also faketouchable.
+ if (mIsTouchDevice && !mIsFakeTouchDevice) {
+ throw new IllegalStateException(
+ "touchscreen supported device must report faketouch.");
+ }
return mIsFakeTouchDevice;
}
}
@@ -8576,7 +8580,7 @@
}
if (win.mActivityRecord == null || !win.mActivityRecord.isState(
- Task.ActivityState.RESUMED)) {
+ ActivityRecord.State.RESUMED)) {
mDisplayHashController.sendDisplayHashError(callback,
DISPLAY_HASH_ERROR_MISSING_WINDOW);
return;
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index a82a478..29c6391 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -480,7 +480,7 @@
} else if (!task.mCreatedByOrganizer) {
throw new UnsupportedOperationException(
"Cannot set non-organized task as adjacent flag root: " + wc);
- } else if (task.mAdjacentTask == null) {
+ } else if (task.getAdjacentTaskFragment() == null) {
throw new UnsupportedOperationException(
"Cannot set non-adjacent task as adjacent flag root: " + wc);
}
@@ -710,7 +710,7 @@
throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: Not created by"
+ " organizer root1=" + root1 + " root2=" + root2);
}
- root1.setAdjacentTask(root2);
+ root1.setAdjacentTaskFragment(root2);
return TRANSACT_EFFECTS_LIFECYCLE;
}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 26cfbdf..bee722e 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -25,6 +25,13 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.server.am.ActivityManagerService.MY_PID;
+import static com.android.server.wm.ActivityRecord.State.DESTROYED;
+import static com.android.server.wm.ActivityRecord.State.DESTROYING;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STARTED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
@@ -32,13 +39,6 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskManagerService.INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MILLIS;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
-import static com.android.server.wm.Task.ActivityState.DESTROYED;
-import static com.android.server.wm.Task.ActivityState.DESTROYING;
-import static com.android.server.wm.Task.ActivityState.PAUSED;
-import static com.android.server.wm.Task.ActivityState.PAUSING;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
-import static com.android.server.wm.Task.ActivityState.STARTED;
-import static com.android.server.wm.Task.ActivityState.STOPPING;
import android.Manifest;
import android.annotation.NonNull;
@@ -735,10 +735,10 @@
if (canUpdate) {
// Make sure the previous top activity in the process no longer be resumed.
if (mPreQTopResumedActivity != null && mPreQTopResumedActivity.isState(RESUMED)) {
- final Task task = mPreQTopResumedActivity.getTask();
- if (task != null) {
- boolean userLeaving = task.shouldBeVisible(null);
- task.startPausingLocked(userLeaving, false /* uiSleeping */,
+ final TaskFragment taskFrag = mPreQTopResumedActivity.getTaskFragment();
+ if (taskFrag != null) {
+ boolean userLeaving = taskFrag.shouldBeVisible(null);
+ taskFrag.startPausing(userLeaving, false /* uiSleeping */,
activity, "top-resumed-changed");
}
}
@@ -989,7 +989,7 @@
// Since there could be more than one activities in a process record, we don't need to
// compute the OomAdj with each of them, just need to find out the activity with the
// "best" state, the order would be visible, pausing, stopping...
- Task.ActivityState bestInvisibleState = DESTROYED;
+ ActivityRecord.State bestInvisibleState = DESTROYED;
boolean allStoppingFinishing = true;
boolean visible = false;
int minTaskLayer = Integer.MAX_VALUE;
@@ -1213,12 +1213,12 @@
hasVisibleActivities = true;
}
- final Task task = r.getTask();
- if (task != null) {
+ final TaskFragment taskFragment = r.getTaskFragment();
+ if (taskFragment != null) {
// There may be a pausing activity that hasn't shown any window and was requested
// to be hidden. But pausing is also a visible state, it should be regarded as
// visible, so the caller can know the next activity should be resumed.
- hasVisibleActivities |= task.handleAppDied(this);
+ hasVisibleActivities |= taskFragment.handleAppDied(this);
}
r.handleAppDied();
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index d487483..ad89e96 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -103,6 +103,7 @@
import com.android.internal.widget.ILockSettings;
import com.android.server.am.ActivityManagerService;
import com.android.server.appbinding.AppBindingService;
+import com.android.server.art.ArtManagerLocal;
import com.android.server.attention.AttentionManagerService;
import com.android.server.audio.AudioService;
import com.android.server.biometrics.AuthService;
@@ -2631,6 +2632,10 @@
mSystemServiceManager.startService(GAME_MANAGER_SERVICE_CLASS);
t.traceEnd();
+ t.traceBegin("ArtManagerLocal");
+ LocalManagerRegistry.addManager(ArtManagerLocal.class, new ArtManagerLocal());
+ t.traceEnd();
+
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_UWB)) {
t.traceBegin("UwbService");
mSystemServiceManager.startService(UWB_SERVICE_CLASS);
diff --git a/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java b/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
index aca48b6..ee5a534 100644
--- a/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
@@ -19,8 +19,8 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageParser.SigningDetails;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.SigningInfo;
import android.os.Build;
import android.os.Environment;
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index f2bb47b..1136cac 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -90,11 +90,6 @@
super(context);
mAppStateTracker = mock(AppStateTrackerImpl.class);
}
-
- @Override
- public boolean isChainedAttributionEnabled() {
- return false;
- }
}
@Before
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index db0c3ae..d4f17f3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -21,10 +21,10 @@
import android.content.pm.ApplicationInfo
import android.content.pm.FallbackCategoryProvider
import android.content.pm.FeatureInfo
-import android.content.pm.PackageParser.SigningDetails
import android.content.pm.ResolveInfo
import android.content.pm.ServiceInfo
import android.content.pm.Signature
+import android.content.pm.SigningDetails
import android.content.pm.UserInfo
import android.content.pm.parsing.ParsingPackage
import android.content.pm.parsing.ParsingPackageUtils
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
index 7d628be..e1eb34e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
@@ -227,8 +227,8 @@
assertThat(destroyedNonReadySession.isDestroyed()).isTrue();
mStagingManager.onBootCompletedBroadcastReceived();
- assertThat(nonReadyApkSession.hasPreRebootVerificationStarted()).isTrue();
- assertThat(nonReadyApexSession.hasPreRebootVerificationStarted()).isTrue();
+ assertThat(nonReadyApkSession.hasVerificationStarted()).isTrue();
+ assertThat(nonReadyApexSession.hasVerificationStarted()).isTrue();
}
@Test
@@ -520,7 +520,7 @@
private int mParentSessionId = -1;
private String mPackageName;
private boolean mIsAbandonded = false;
- private boolean mPreRebootVerificationStarted = false;
+ private boolean mVerificationStarted = false;
private final List<StagingManager.StagedSession> mChildSessions = new ArrayList<>();
private FakeStagedSession(int sessionId) {
@@ -551,8 +551,8 @@
return mIsAbandonded;
}
- private boolean hasPreRebootVerificationStarted() {
- return mPreRebootVerificationStarted;
+ private boolean hasVerificationStarted() {
+ return mVerificationStarted;
}
private FakeStagedSession addChildSession(FakeStagedSession session) {
@@ -707,20 +707,13 @@
}
@Override
- public boolean notifyStartPreRebootVerification() {
- mPreRebootVerificationStarted = true;
- // TODO(ioffe): change to true when tests for pre-reboot verification are added.
- return false;
- }
-
- @Override
public void notifyEndPreRebootVerification() {
throw new UnsupportedOperationException();
}
@Override
public void verifySession() {
- throw new UnsupportedOperationException();
+ mVerificationStarted = true;
}
}
}
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 339a5f9..4abc7ee 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -117,6 +117,7 @@
":PackageParserTestApp2",
":PackageParserTestApp3",
":PackageParserTestApp4",
+ ":PackageParserTestApp5",
":apex.test",
":test.rebootless_apex_v1",
":test.rebootless_apex_v2",
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index d6c11a5..e612d12 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -65,6 +65,7 @@
import static org.mockito.Mockito.when;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.content.ComponentName;
import android.content.Context;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
index ee00cb2..7781459 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
@@ -617,7 +617,7 @@
mA11ySecurityPolicy.checkAccessibilityAccess(mMockA11yServiceConnection);
verify(mMockAppOpsManager).noteOpNoThrow(AppOpsManager.OPSTR_ACCESS_ACCESSIBILITY,
- APP_UID, PACKAGE_NAME);
+ APP_UID, PACKAGE_NAME, null, null);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index 00daa5c..432a500 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -29,6 +29,7 @@
import static org.mockito.Mockito.when;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.GestureDescription;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.content.ComponentName;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
index e4d51e4..4afe099 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
@@ -120,6 +120,7 @@
@Mock private AccessibilityWindowManager.AccessibilityEventSender mMockA11yEventSender;
@Mock private AccessibilitySecurityPolicy mMockA11ySecurityPolicy;
@Mock private AccessibilitySecurityPolicy.AccessibilityUserManager mMockA11yUserManager;
+ @Mock private AccessibilityTraceManager mMockA11yTraceManager;
@Mock private IBinder mMockHostToken;
@Mock private IBinder mMockEmbeddedToken;
@@ -140,7 +141,8 @@
mMockWindowManagerInternal,
mMockA11yEventSender,
mMockA11ySecurityPolicy,
- mMockA11yUserManager);
+ mMockA11yUserManager,
+ mMockA11yTraceManager);
// Starts tracking window of default display and sets the default display
// as top focused display before each testing starts.
startTrackingPerDisplay(Display.DEFAULT_DISPLAY);
@@ -834,6 +836,19 @@
assertNull(token);
}
+ @Test
+ public void onDisplayReparented_shouldRemoveObserver() throws RemoteException {
+ // Starts tracking window of second display.
+ startTrackingPerDisplay(SECONDARY_DISPLAY_ID);
+ assertTrue(mA11yWindowManager.isTrackingWindowsLocked(SECONDARY_DISPLAY_ID));
+ // Notifies the second display is an embedded one of the default display.
+ final WindowsForAccessibilityCallback callbacks =
+ mCallbackOfWindows.get(Display.DEFAULT_DISPLAY);
+ callbacks.onDisplayReparented(SECONDARY_DISPLAY_ID);
+ // Makes sure the observer of the second display is removed.
+ assertFalse(mA11yWindowManager.isTrackingWindowsLocked(SECONDARY_DISPLAY_ID));
+ }
+
private void registerLeashedTokenAndWindowId() {
mA11yWindowManager.registerIdLocked(mMockHostToken, HOST_WINDOW_ID);
mA11yWindowManager.registerIdLocked(mMockEmbeddedToken, EMBEDDED_WINDOW_ID);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
index 78e651b..c62cae5 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
@@ -56,11 +56,13 @@
private MessageCapturingHandler mHandler = new MessageCapturingHandler(
msg -> mInterceptor.handleMessage(msg));
@Mock AccessibilityManagerService mMockAms;
+ @Mock AccessibilityTraceManager mMockTraceManager;
@Mock WindowManagerPolicy mMockPolicy;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ when(mMockAms.getTraceManager()).thenReturn(mMockTraceManager);
mInterceptor = new KeyboardInterceptor(mMockAms, mMockPolicy, mHandler);
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
index 1ac4a8e..2df7b55 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
@@ -113,6 +113,7 @@
MotionEventInjector mMotionEventInjector;
IAccessibilityServiceClient mServiceInterface;
+ AccessibilityTraceManager mTrace;
List<GestureStep> mLineList = new ArrayList<>();
List<GestureStep> mClickList = new ArrayList<>();
List<GestureStep> mContinuedLineList1 = new ArrayList<>();
@@ -139,7 +140,8 @@
return mMotionEventInjector.handleMessage(msg);
}
});
- mMotionEventInjector = new MotionEventInjector(mMessageCapturingHandler);
+ mTrace = mock(AccessibilityTraceManager.class);
+ mMotionEventInjector = new MotionEventInjector(mMessageCapturingHandler, mTrace);
mServiceInterface = mock(IAccessibilityServiceClient.class);
mLineList = createSimpleGestureFromPoints(0, 0, false, LINE_DURATION, LINE_START, LINE_END);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
index 1603087..4ce9ba0 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
@@ -27,6 +27,7 @@
import static org.mockito.Mockito.when;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.app.UiAutomation;
import android.content.Context;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
index 7bf0bb8..4a06611f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
@@ -36,6 +36,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.accessibilityservice.AccessibilityGestureEvent;
import android.accessibilityservice.AccessibilityService;
@@ -53,6 +54,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.server.accessibility.AccessibilityManagerService;
+import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.accessibility.EventStreamTransformation;
import com.android.server.accessibility.utils.GestureLogParser;
import com.android.server.testutils.OffsettableClock;
@@ -107,6 +109,8 @@
@Mock
private AccessibilityManagerService mMockAms;
+ @Mock
+ private AccessibilityTraceManager mMockTraceManager;
@Captor
private ArgumentCaptor<AccessibilityGestureEvent> mGestureCaptor;
@@ -143,9 +147,9 @@
if (Looper.myLooper() == null) {
Looper.prepare();
}
+ when(mMockAms.getTraceManager()).thenReturn(mMockTraceManager);
mContext = InstrumentationRegistry.getContext();
mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
- AccessibilityManagerService ams = new AccessibilityManagerService(mContext);
mCaptor = new EventCaptor();
mHandler = new TestHandler();
mTouchExplorer = new TouchExplorer(mContext, mMockAms, null, mHandler);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
index 502f64a..8d90512 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
@@ -52,6 +52,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.server.accessibility.AccessibilityManagerService;
+import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.accessibility.test.MessageCapturingHandler;
import com.android.server.wm.WindowManagerInternal;
import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks;
@@ -93,6 +94,7 @@
mock(FullScreenMagnificationController.ControllerContext.class);
final Context mMockContext = mock(Context.class);
final AccessibilityManagerService mMockAms = mock(AccessibilityManagerService.class);
+ final AccessibilityTraceManager mMockTraceManager = mock(AccessibilityTraceManager.class);
final WindowManagerInternal mMockWindowManager = mock(WindowManagerInternal.class);
private final MagnificationAnimationCallback mAnimationCallback = mock(
MagnificationAnimationCallback.class);
@@ -113,9 +115,11 @@
when(mMockContext.getMainLooper()).thenReturn(looper);
when(mMockControllerCtx.getContext()).thenReturn(mMockContext);
when(mMockControllerCtx.getAms()).thenReturn(mMockAms);
+ when(mMockControllerCtx.getTraceManager()).thenReturn(mMockTraceManager);
when(mMockControllerCtx.getWindowManager()).thenReturn(mMockWindowManager);
when(mMockControllerCtx.getHandler()).thenReturn(mMessageCapturingHandler);
when(mMockControllerCtx.getAnimationDuration()).thenReturn(1000L);
+ when(mMockAms.getTraceManager()).thenReturn(mMockTraceManager);
initMockWindowManager();
mFullScreenMagnificationController = new FullScreenMagnificationController(
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index f881f04..b14c353 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -52,6 +52,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.server.accessibility.AccessibilityManagerService;
+import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.accessibility.EventStreamTransformation;
import com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationInfoChangedCallback;
import com.android.server.testutils.OffsettableClock;
@@ -129,6 +130,10 @@
MagnificationInfoChangedCallback mMagnificationInfoChangedCallback;
@Mock
WindowMagnificationPromptController mWindowMagnificationPromptController;
+ @Mock
+ AccessibilityManagerService mMockAccessibilityManagerService;
+ @Mock
+ AccessibilityTraceManager mMockTraceManager;
private OffsettableClock mClock;
private FullScreenMagnificationGestureHandler mMgh;
@@ -144,7 +149,9 @@
mock(FullScreenMagnificationController.ControllerContext.class);
final WindowManagerInternal mockWindowManager = mock(WindowManagerInternal.class);
when(mockController.getContext()).thenReturn(mContext);
- when(mockController.getAms()).thenReturn(mock(AccessibilityManagerService.class));
+ when(mockController.getAms()).thenReturn(mMockAccessibilityManagerService);
+ when(mMockAccessibilityManagerService.getTraceManager()).thenReturn(mMockTraceManager);
+ when(mockController.getTraceManager()).thenReturn(mMockTraceManager);
when(mockController.getWindowManager()).thenReturn(mockWindowManager);
when(mockController.getHandler()).thenReturn(new Handler(mContext.getMainLooper()));
when(mockController.newValueAnimator()).thenReturn(new ValueAnimator());
@@ -179,7 +186,7 @@
private FullScreenMagnificationGestureHandler newInstance(boolean detectTripleTap,
boolean detectShortcutTrigger) {
FullScreenMagnificationGestureHandler h = new FullScreenMagnificationGestureHandler(
- mContext, mFullScreenMagnificationController, mMockCallback,
+ mContext, mFullScreenMagnificationController, mMockTraceManager, mMockCallback,
detectTripleTap, detectShortcutTrigger,
mWindowMagnificationPromptController, DISPLAY_0);
mHandler = new TestHandler(h.mDetectingState, mClock) {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index b7f5f4d..e82adc8 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -54,6 +54,7 @@
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.accessibility.AccessibilityManagerService;
+import com.android.server.accessibility.AccessibilityTraceManager;
import org.junit.After;
import org.junit.Before;
@@ -84,6 +85,8 @@
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
@Mock
+ private AccessibilityTraceManager mTraceManager;
+ @Mock
private AccessibilityManagerService mService;
@Mock
private MagnificationController.TransitionCallBack mTransitionCallBack;
@@ -112,7 +115,7 @@
CURRENT_USER_ID);
mWindowMagnificationManager = Mockito.spy(
new WindowMagnificationManager(mContext, CURRENT_USER_ID,
- mock(WindowMagnificationManager.Callback.class)));
+ mock(WindowMagnificationManager.Callback.class), mTraceManager));
mMockConnection = new MockWindowMagnificationConnection(true);
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
mScreenMagnificationControllerStubber = new FullScreenMagnificationControllerStubber(
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java
index 514d16a..ef6ed88 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java
@@ -31,6 +31,8 @@
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.accessibility.AccessibilityTraceManager;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -49,6 +51,8 @@
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
@Mock
+ AccessibilityTraceManager mTraceManager;
+ @Mock
MagnificationGestureHandler.Callback mCallback;
@Before
@@ -57,6 +61,7 @@
mMgh = new TestMagnificationGestureHandler(DISPLAY_0,
/* detectTripleTap= */true,
/* detectShortcutTrigger= */true,
+ mTraceManager,
mCallback);
}
@@ -129,8 +134,9 @@
boolean mIsInternalMethodCalled = false;
TestMagnificationGestureHandler(int displayId, boolean detectTripleTap,
- boolean detectShortcutTrigger, @NonNull Callback callback) {
- super(displayId, detectTripleTap, detectShortcutTrigger, callback);
+ boolean detectShortcutTrigger, @NonNull AccessibilityTraceManager trace,
+ @NonNull Callback callback) {
+ super(displayId, detectTripleTap, detectShortcutTrigger, trace, callback);
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java
index c88bc3b..1638563 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java
@@ -29,6 +29,8 @@
import android.view.accessibility.IWindowMagnificationConnectionCallback;
import android.view.accessibility.MagnificationAnimationCallback;
+import com.android.server.accessibility.AccessibilityTraceManager;
+
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
@@ -45,6 +47,8 @@
private IWindowMagnificationConnection mConnection;
@Mock
+ private AccessibilityTraceManager mTrace;
+ @Mock
private IWindowMagnificationConnectionCallback mCallback;
@Mock
private MagnificationAnimationCallback mAnimationCallback;
@@ -57,7 +61,7 @@
MockitoAnnotations.initMocks(this);
mMockWindowMagnificationConnection = new MockWindowMagnificationConnection();
mConnection = mMockWindowMagnificationConnection.getConnection();
- mConnectionWrapper = new WindowMagnificationConnectionWrapper(mConnection);
+ mConnectionWrapper = new WindowMagnificationConnectionWrapper(mConnection, mTrace);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
index b9498d6..6a5aae6 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
@@ -35,6 +35,7 @@
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.accessibility.EventStreamTransformation;
import com.android.server.accessibility.utils.TouchEventGenerator;
@@ -74,16 +75,18 @@
private WindowMagnificationGestureHandler mWindowMagnificationGestureHandler;
@Mock
MagnificationGestureHandler.Callback mMockCallback;
+ @Mock
+ AccessibilityTraceManager mMockTrace;
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
mContext = InstrumentationRegistry.getInstrumentation().getContext();
mWindowMagnificationManager = new WindowMagnificationManager(mContext, 0,
- mock(WindowMagnificationManager.Callback.class));
+ mock(WindowMagnificationManager.Callback.class), mMockTrace);
mMockConnection = new MockWindowMagnificationConnection();
mWindowMagnificationGestureHandler = new WindowMagnificationGestureHandler(
- mContext, mWindowMagnificationManager, mMockCallback,
+ mContext, mWindowMagnificationManager, mMockTrace, mMockCallback,
/** detectTripleTap= */true, /** detectShortcutTrigger= */true, DISPLAY_0);
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
mWindowMagnificationGestureHandler.setNext(strictMock(EventStreamTransformation.class));
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
index a20272a..af6d40f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -52,6 +52,7 @@
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.LocalServices;
+import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.statusbar.StatusBarManagerInternal;
import org.junit.Before;
@@ -72,6 +73,8 @@
@Mock
private Context mContext;
@Mock
+ private AccessibilityTraceManager mMockTrace;
+ @Mock
private StatusBarManagerInternal mMockStatusBarManagerInternal;
@Mock
private MagnificationAnimationCallback mAnimationCallback;
@@ -88,7 +91,7 @@
mResolver = new MockContentResolver();
mMockConnection = new MockWindowMagnificationConnection();
mWindowMagnificationManager = new WindowMagnificationManager(mContext, CURRENT_USER_ID,
- mMockCallback);
+ mMockCallback, mMockTrace);
when(mContext.getContentResolver()).thenReturn(mResolver);
doAnswer((InvocationOnMock invocation) -> {
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
index 7323096..7aea65e 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
@@ -20,7 +20,6 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.app.backup.BackupManager.OperationType;
@@ -30,8 +29,8 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.Property;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.SigningInfo;
import android.os.Process;
import android.os.UserHandle;
@@ -41,7 +40,6 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.server.backup.UserBackupManagerService;
-import com.android.server.pm.parsing.pkg.AndroidPackage;
import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
@@ -540,9 +538,9 @@
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -558,9 +556,9 @@
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -635,9 +633,9 @@
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -656,9 +654,9 @@
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -677,9 +675,9 @@
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {signature1Copy, signature2Copy},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -698,9 +696,9 @@
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -719,9 +717,9 @@
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -743,9 +741,9 @@
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_2},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
new Signature[] {SIGNATURE_1, SIGNATURE_2}));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -770,9 +768,9 @@
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_2},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
new Signature[] {SIGNATURE_1, SIGNATURE_2}));
packageInfo.applicationInfo = new ApplicationInfo();
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
index 5fcce67..e2536f8 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
@@ -39,8 +39,8 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.SigningInfo;
import android.os.Bundle;
import android.os.Process;
@@ -376,9 +376,9 @@
packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
packageInfo.applicationInfo.backupAgentName = null;
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {FAKE_SIGNATURE_2},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
PackageManagerStub.sPackageInfo = packageInfo;
@@ -413,9 +413,9 @@
packageInfo.applicationInfo.uid = Process.SYSTEM_UID;
packageInfo.applicationInfo.backupAgentName = "backup.agent";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {FAKE_SIGNATURE_1},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
PackageManagerStub.sPackageInfo = packageInfo;
@@ -451,9 +451,9 @@
packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
packageInfo.applicationInfo.backupAgentName = null;
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {FAKE_SIGNATURE_1},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
PackageManagerStub.sPackageInfo = packageInfo;
@@ -492,9 +492,9 @@
packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
packageInfo.applicationInfo.backupAgentName = null;
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {FAKE_SIGNATURE_1},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.versionCode = 2;
@@ -536,9 +536,9 @@
packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
packageInfo.applicationInfo.backupAgentName = null;
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {FAKE_SIGNATURE_1},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.versionCode = 1;
@@ -576,9 +576,9 @@
packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
packageInfo.applicationInfo.backupAgentName = null;
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {FAKE_SIGNATURE_1},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.versionCode = 1;
diff --git a/services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java b/services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java
index a19b387..363c26b 100644
--- a/services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java
@@ -21,7 +21,6 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -53,9 +52,7 @@
import com.android.server.twilight.TwilightState;
import org.junit.After;
-import org.junit.AfterClass;
import org.junit.Before;
-import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
@@ -76,25 +73,29 @@
private int mUserId;
private MockTwilightManager mTwilightManager;
+ private DisplayTransformManager mDisplayTransformManager;
private ColorDisplayService mCds;
private ColorDisplayService.BinderService mBinderService;
private Resources mResourcesSpy;
- @BeforeClass
- public static void setDtm() {
- final DisplayTransformManager dtm = Mockito.mock(DisplayTransformManager.class);
- LocalServices.addService(DisplayTransformManager.class, dtm);
- }
+ private static final int[] MINIMAL_COLOR_MODES = new int[] {
+ ColorDisplayManager.COLOR_MODE_NATURAL,
+ ColorDisplayManager.COLOR_MODE_BOOSTED,
+ };
@Before
public void setUp() {
mContext = Mockito.spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
doReturn(mContext).when(mContext).getApplicationContext();
- mResourcesSpy = Mockito.spy(mContext.getResources());
- when(mContext.getResources()).thenReturn(mResourcesSpy);
+ final Resources res = Mockito.spy(mContext.getResources());
+ doReturn(MINIMAL_COLOR_MODES).when(res).getIntArray(R.array.config_availableColorModes);
+ doReturn(true).when(res).getBoolean(R.bool.config_nightDisplayAvailable);
+ doReturn(true).when(res).getBoolean(R.bool.config_displayWhiteBalanceAvailable);
+ when(mContext.getResources()).thenReturn(res);
+ mResourcesSpy = res;
mUserId = ActivityManager.getCurrentUser();
@@ -108,6 +109,10 @@
mTwilightManager = new MockTwilightManager();
LocalServices.addService(TwilightManager.class, mTwilightManager);
+ mDisplayTransformManager = Mockito.mock(DisplayTransformManager.class);
+ doReturn(true).when(mDisplayTransformManager).needsLinearColorMatrix();
+ LocalServices.addService(DisplayTransformManager.class, mDisplayTransformManager);
+
mCds = new ColorDisplayService(mContext);
mBinderService = mCds.new BinderService();
LocalServices.addService(ColorDisplayService.ColorDisplayServiceInternal.class,
@@ -116,12 +121,18 @@
@After
public void tearDown() {
- LocalServices.removeServiceForTest(TwilightManager.class);
-
+ /*
+ * Wait for internal {@link Handler} to finish processing pending messages, so that test
+ * code can safelyremove {@link DisplayTransformManager} mock from {@link LocalServices}.
+ */
+ mCds.mHandler.runWithScissors(() -> { /* nop */ }, /* timeout */ 1000);
mCds = null;
+ LocalServices.removeServiceForTest(TwilightManager.class);
mTwilightManager = null;
+ LocalServices.removeServiceForTest(DisplayTransformManager.class);
+
mUserId = UserHandle.USER_NULL;
mContext = null;
@@ -130,11 +141,6 @@
LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class);
}
- @AfterClass
- public static void removeDtm() {
- LocalServices.removeServiceForTest(DisplayTransformManager.class);
- }
-
@Test
public void customSchedule_whenStartedAfterNight_ifOffAfterNight_turnsOff() {
setAutoModeCustom(-120 /* startTimeOffset */, -60 /* endTimeOffset */);
@@ -1064,24 +1070,18 @@
@Test
public void compositionColorSpaces_noResources() {
- final DisplayTransformManager dtm = LocalServices.getService(DisplayTransformManager.class);
- reset(dtm);
-
when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorModes))
.thenReturn(new int[] {});
when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorSpaces))
.thenReturn(new int[] {});
setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
startService();
- verify(dtm).setColorMode(eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(),
- eq(Display.COLOR_MODE_INVALID));
+ verify(mDisplayTransformManager).setColorMode(
+ eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(), eq(Display.COLOR_MODE_INVALID));
}
@Test
public void compositionColorSpaces_invalidResources() {
- final DisplayTransformManager dtm = LocalServices.getService(DisplayTransformManager.class);
- reset(dtm);
-
when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorModes))
.thenReturn(new int[] {
ColorDisplayManager.COLOR_MODE_NATURAL,
@@ -1094,15 +1094,12 @@
});
setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
startService();
- verify(dtm).setColorMode(eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(),
- eq(Display.COLOR_MODE_INVALID));
+ verify(mDisplayTransformManager).setColorMode(
+ eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(), eq(Display.COLOR_MODE_INVALID));
}
@Test
public void compositionColorSpaces_validResources_validColorMode() {
- final DisplayTransformManager dtm = LocalServices.getService(DisplayTransformManager.class);
- reset(dtm);
-
when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorModes))
.thenReturn(new int[] {
ColorDisplayManager.COLOR_MODE_NATURAL
@@ -1113,15 +1110,12 @@
});
setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
startService();
- verify(dtm).setColorMode(eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(),
- eq(Display.COLOR_MODE_SRGB));
+ verify(mDisplayTransformManager).setColorMode(
+ eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(), eq(Display.COLOR_MODE_SRGB));
}
@Test
public void compositionColorSpaces_validResources_invalidColorMode() {
- final DisplayTransformManager dtm = LocalServices.getService(DisplayTransformManager.class);
- reset(dtm);
-
when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorModes))
.thenReturn(new int[] {
ColorDisplayManager.COLOR_MODE_NATURAL
@@ -1132,8 +1126,8 @@
});
setColorMode(ColorDisplayManager.COLOR_MODE_BOOSTED);
startService();
- verify(dtm).setColorMode(eq(ColorDisplayManager.COLOR_MODE_BOOSTED), any(),
- eq(Display.COLOR_MODE_INVALID));
+ verify(mDisplayTransformManager).setColorMode(
+ eq(ColorDisplayManager.COLOR_MODE_BOOSTED), any(), eq(Display.COLOR_MODE_INVALID));
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
index 6cc8d471..a38a84b 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
@@ -45,6 +45,7 @@
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Collections;
/** Tests for {@link ActiveSourceAction} */
@SmallTest
@@ -76,7 +77,7 @@
mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
- mHdmiControlService = new HdmiControlService(mContextSpy) {
+ mHdmiControlService = new HdmiControlService(mContextSpy, Collections.emptyList()) {
@Override
AudioManager getAudioManager() {
return new AudioManager() {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
index 97bd066..5cf81ec 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
@@ -45,6 +45,7 @@
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Collections;
/** Tests for {@link ArcInitiationActionFromAvrTest} */
@SmallTest
@@ -79,7 +80,7 @@
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
HdmiControlService hdmiControlService =
- new HdmiControlService(mContextSpy) {
+ new HdmiControlService(mContextSpy, Collections.emptyList()) {
@Override
boolean isPowerStandby() {
return false;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
index 29c9b40..1462839 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
@@ -45,6 +45,7 @@
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Collections;
/** Tests for {@link ArcTerminationActionFromAvr} */
@SmallTest
@@ -80,7 +81,7 @@
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
HdmiControlService hdmiControlService =
- new HdmiControlService(mContextSpy) {
+ new HdmiControlService(mContextSpy, Collections.emptyList()) {
@Override
void wakeUp() {
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
index 8b23be5..f49b1c1 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
@@ -34,6 +34,8 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.Collections;
+
/** Tests for {@link DetectTvSystemAudioModeSupportAction} class. */
@SmallTest
@Presubmit
@@ -53,7 +55,8 @@
public void SetUp() {
mDeviceInfoForTests = new HdmiDeviceInfo(1001, 1234);
HdmiControlService hdmiControlService =
- new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
+ new HdmiControlService(InstrumentationRegistry.getTargetContext(),
+ Collections.emptyList()) {
@Override
void sendCecCommand(
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
index 650ffe9..fe8d691 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
@@ -51,6 +51,7 @@
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Collections;
/** Tests for {@link DevicePowerStatusAction} */
@SmallTest
@@ -88,7 +89,7 @@
mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
- mHdmiControlService = new HdmiControlService(mContextSpy) {
+ mHdmiControlService = new HdmiControlService(mContextSpy, Collections.emptyList()) {
@Override
AudioManager getAudioManager() {
return new AudioManager() {
@@ -220,6 +221,7 @@
mHdmiControlService.getHdmiCecConfig().setIntValue(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
HdmiControlManager.HDMI_CEC_VERSION_2_0);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mPlaybackDevice.addAndStartAction(mDevicePowerStatusAction);
mTestLooper.dispatchAll();
@@ -240,6 +242,7 @@
mHdmiControlService.getHdmiCecConfig().setIntValue(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
HdmiControlManager.HDMI_CEC_VERSION_2_0);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
HdmiCecMessage reportPhysicalAddress = HdmiCecMessageBuilder
.buildReportPhysicalAddressCommand(ADDR_TV, 0x0000, HdmiDeviceInfo.DEVICE_TV);
mNativeWrapper.onCecMessage(reportPhysicalAddress);
@@ -263,6 +266,7 @@
mHdmiControlService.getHdmiCecConfig().setIntValue(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
HdmiControlManager.HDMI_CEC_VERSION_2_0);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
HdmiCecMessage reportPhysicalAddress = HdmiCecMessageBuilder
.buildReportPhysicalAddressCommand(ADDR_TV, 0x0000, HdmiDeviceInfo.DEVICE_TV);
mNativeWrapper.onCecMessage(reportPhysicalAddress);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java
index fa5cb67..0d5d05c 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java
@@ -54,6 +54,7 @@
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Collections;
@SmallTest
@RunWith(JUnit4.class)
@@ -105,7 +106,8 @@
mMyLooper = mTestLooper.getLooper();
mHdmiControlService =
- new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
+ new HdmiControlService(InstrumentationRegistry.getTargetContext(),
+ Collections.emptyList()) {
@Override
boolean isControlEnabled() {
return true;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
index 41946fb..abc1468e 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
@@ -69,12 +69,27 @@
R.bool.config_cecHdmiCecVersion20_default);
doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRoutingControl_userConfigurable);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRoutingControlEnabled_allowed);
+ doReturn(false).when(resources).getBoolean(
+ R.bool.config_cecRoutingControlEnabled_default);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRoutingControlDisabled_allowed);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRoutingControlDisabled_default);
+
+ doReturn(true).when(resources).getBoolean(
R.bool.config_cecPowerControlMode_userConfigurable);
doReturn(true).when(resources).getBoolean(
R.bool.config_cecPowerControlModeTv_allowed);
- doReturn(true).when(resources).getBoolean(
+ doReturn(false).when(resources).getBoolean(
R.bool.config_cecPowerControlModeTv_default);
doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecPowerControlModeTvAndAudioSystem_allowed);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecPowerControlModeTvAndAudioSystem_default);
+ doReturn(true).when(resources).getBoolean(
R.bool.config_cecPowerControlModeBroadcast_allowed);
doReturn(false).when(resources).getBoolean(
R.bool.config_cecPowerControlModeBroadcast_default);
@@ -95,6 +110,17 @@
R.bool.config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default);
doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecSystemAudioControl_userConfigurable);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecSystemAudioControlEnabled_allowed);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecSystemAudioControlEnabled_default);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecSystemAudioControlDisabled_allowed);
+ doReturn(false).when(resources).getBoolean(
+ R.bool.config_cecSystemAudioControlDisabled_default);
+
+ doReturn(true).when(resources).getBoolean(
R.bool.config_cecSystemAudioModeMuting_userConfigurable);
doReturn(true).when(resources).getBoolean(
R.bool.config_cecSystemAudioModeMutingEnabled_allowed);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
index 29f62b5..da10ec4 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
@@ -25,6 +25,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
@@ -61,6 +62,7 @@
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Collections;
/**
* Tests for the {@link HdmiCecAtomWriter} class and its usage by the HDMI-CEC framework.
@@ -103,7 +105,7 @@
mIThermalServiceMock, new Handler(mLooper)));
doReturn(true).when(mIPowerManagerMock).isInteractive();
- mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy));
+ mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy, Collections.emptyList()));
doNothing().when(mHdmiControlServiceSpy)
.writeStringSystemProperty(anyString(), anyString());
doReturn(mHdmiCecAtomWriterSpy).when(mHdmiControlServiceSpy).getAtomWriter();
@@ -200,7 +202,7 @@
mTestLooper.dispatchAll();
- verify(mHdmiCecAtomWriterSpy, times(1)).messageReported(
+ verify(mHdmiCecAtomWriterSpy, atLeastOnce()).messageReported(
any(),
anyInt(),
eq(callerUid),
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
index a37f94b..a94690e 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
@@ -82,8 +82,10 @@
assertThat(hdmiCecConfig.getAllSettings())
.containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+ HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL,
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL,
HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
@@ -103,8 +105,10 @@
assertThat(hdmiCecConfig.getUserSettings())
.containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+ HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL,
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL,
HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
@@ -125,7 +129,9 @@
assertThat(hdmiCecConfig.getUserSettings())
.containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+ HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL,
HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL,
HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
@@ -188,6 +194,7 @@
assertThat(hdmiCecConfig.getAllowedStringValues(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE))
.containsExactly(HdmiControlManager.POWER_CONTROL_MODE_TV,
+ HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM,
HdmiControlManager.POWER_CONTROL_MODE_BROADCAST,
HdmiControlManager.POWER_CONTROL_MODE_NONE);
}
@@ -199,6 +206,7 @@
assertThat(hdmiCecConfig.getAllowedStringValues(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE))
.containsExactly(HdmiControlManager.POWER_CONTROL_MODE_TV,
+ HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM,
HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
}
@@ -255,12 +263,12 @@
HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThat(hdmiCecConfig.getDefaultStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE))
- .isEqualTo(HdmiControlManager.POWER_CONTROL_MODE_TV);
+ .isEqualTo(HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM);
}
@Test
public void getDefaultStringValue_WithOverride() {
- setBooleanResource(R.bool.config_cecPowerControlModeTv_default, false);
+ setBooleanResource(R.bool.config_cecPowerControlModeTvAndAudioSystem_default, false);
setBooleanResource(R.bool.config_cecPowerControlModeBroadcast_default, true);
HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThat(hdmiCecConfig.getDefaultStringValue(
@@ -277,7 +285,7 @@
@Test
public void getDefaultStringValue_NoDefault() {
- setBooleanResource(R.bool.config_cecPowerControlModeTv_default, false);
+ setBooleanResource(R.bool.config_cecPowerControlModeTvAndAudioSystem_default, false);
assertThrows(RuntimeException.class,
() -> new HdmiCecConfig(mContext, mStorageAdapter));
}
@@ -334,7 +342,7 @@
public void getStringValue_GlobalSetting_BasicSanity() {
when(mStorageAdapter.retrieveGlobalSetting(
Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.POWER_CONTROL_MODE_TV))
+ HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM))
.thenReturn(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThat(hdmiCecConfig.getStringValue(
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
index ee1a857..bd6e46d 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
@@ -69,6 +69,7 @@
import org.junit.runners.JUnit4;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Optional;
/** Tests for {@link com.android.server.hdmi.HdmiCecController} class. */
@@ -99,7 +100,7 @@
mMyLooper = mTestLooper.getLooper();
mHdmiControlServiceSpy = spy(new HdmiControlService(
- InstrumentationRegistry.getTargetContext()));
+ InstrumentationRegistry.getTargetContext(), Collections.emptyList()));
doReturn(mMyLooper).when(mHdmiControlServiceSpy).getIoLooper();
doReturn(mMyLooper).when(mHdmiControlServiceSpy).getServiceLooper();
doAnswer(__ -> mCecVersion).when(mHdmiControlServiceSpy).getCecVersion();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 7911c40..e03e1be 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -53,6 +53,7 @@
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Collections;
@SmallTest
@Presubmit
@@ -96,7 +97,8 @@
mMyLooper = mTestLooper.getLooper();
mHdmiControlService =
- new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
+ new HdmiControlService(InstrumentationRegistry.getTargetContext(),
+ Collections.emptyList()) {
@Override
AudioManager getAudioManager() {
return new AudioManager() {
@@ -210,7 +212,7 @@
mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
mLocalDevices.add(mHdmiCecLocalDeviceAudioSystem);
mLocalDevices.add(mHdmiCecLocalDevicePlayback);
- mHdmiCecLocalDeviceAudioSystem.setRoutingControlFeatureEnables(true);
+ mHdmiCecLocalDeviceAudioSystem.setRoutingControlFeatureEnabled(true);
mHdmiPortInfo = new HdmiPortInfo[4];
mHdmiPortInfo[0] =
new HdmiPortInfo(
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index 524ad62..99e3d68 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -53,6 +53,7 @@
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.concurrent.TimeUnit;
@SmallTest
@@ -94,7 +95,8 @@
mMyLooper = mTestLooper.getLooper();
mHdmiControlService =
- new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
+ new HdmiControlService(InstrumentationRegistry.getTargetContext(),
+ Collections.emptyList()) {
@Override
void wakeUp() {
mWokenUp = true;
@@ -694,20 +696,45 @@
HdmiControlManager.POWER_CONTROL_MODE_TV);
mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
"HdmiCecLocalDevicePlaybackTest");
- mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
- HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
mTestLooper.dispatchAll();
HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
+ HdmiCecMessage standbyMessageToAudioSystem = HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.mAddress, ADDR_AUDIO_SYSTEM);
HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToAudioSystem);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
+ }
+
+ @Test
+ public void handleOnStandby_ScreenOff_NotActiveSource_ToTvAndAudioSystem() {
+ mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+ HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM);
+ mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
+ "HdmiCecLocalDevicePlaybackTest");
+ mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
+ HdmiCecMessage standbyMessageToAudioSystem = HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.mAddress, ADDR_AUDIO_SYSTEM);
+ HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+ HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+ mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
+
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToAudioSystem);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
}
@@ -719,20 +746,20 @@
HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
"HdmiCecLocalDevicePlaybackTest");
- mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
- HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
mTestLooper.dispatchAll();
HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
+ HdmiCecMessage standbyMessageToAudioSystem = HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.mAddress, ADDR_AUDIO_SYSTEM);
HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToAudioSystem);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
}
@@ -744,20 +771,20 @@
HdmiControlManager.POWER_CONTROL_MODE_NONE);
mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
"HdmiCecLocalDevicePlaybackTest");
- mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
- HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
mTestLooper.dispatchAll();
HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
+ HdmiCecMessage standbyMessageToAudioSystem = HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.mAddress, ADDR_AUDIO_SYSTEM);
HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToAudioSystem);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
}
@@ -769,20 +796,45 @@
HdmiControlManager.POWER_CONTROL_MODE_TV);
mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
- mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
- HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
mTestLooper.dispatchAll();
HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
+ HdmiCecMessage standbyMessageToAudioSystem = HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.mAddress, ADDR_AUDIO_SYSTEM);
HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).contains(standbyMessageToTv);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToAudioSystem);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
+ }
+
+ @Test
+ public void handleOnStandby_ScreenOff_ActiveSource_ToTvAndAudioSystem() {
+ mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+ HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM);
+ mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+ mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+ mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
+ HdmiCecMessage standbyMessageToAudioSystem = HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.mAddress, ADDR_AUDIO_SYSTEM);
+ HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+ HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+ mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(standbyMessageToTv);
+ assertThat(mNativeWrapper.getResultMessages()).contains(standbyMessageToAudioSystem);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
}
@@ -794,20 +846,20 @@
HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
- mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
- HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
mTestLooper.dispatchAll();
HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
+ HdmiCecMessage standbyMessageToAudioSystem = HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.mAddress, ADDR_AUDIO_SYSTEM);
HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToAudioSystem);
assertThat(mNativeWrapper.getResultMessages()).contains(standbyMessageBroadcast);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
}
@@ -819,20 +871,20 @@
HdmiControlManager.POWER_CONTROL_MODE_NONE);
mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
- mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
- HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
mTestLooper.dispatchAll();
HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
+ HdmiCecMessage standbyMessageToAudioSystem = HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.mAddress, ADDR_AUDIO_SYSTEM);
HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToAudioSystem);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
assertThat(mNativeWrapper.getResultMessages()).contains(inactiveSource);
}
@@ -844,9 +896,6 @@
HdmiControlManager.POWER_CONTROL_MODE_TV);
mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
- mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
- HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
mHdmiCecLocalDevicePlayback.onStandby(true, HdmiControlService.STANDBY_SCREEN_OFF);
mTestLooper.dispatchAll();
@@ -1456,7 +1505,7 @@
}
@Test
- public void oneTouchPlay_SendStandbyOnSleepToTv() {
+ public void oneTouchPlay_PowerControlModeToTv() {
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
HdmiControlManager.POWER_CONTROL_MODE_TV);
@@ -1479,7 +1528,30 @@
}
@Test
- public void oneTouchPlay_SendStandbyOnSleepBroadcast() {
+ public void oneTouchPlay_PowerControlModeToTvAndAudioSystem() {
+ mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+ HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM);
+ mHdmiControlService.oneTouchPlay(new IHdmiControlCallback.Stub() {
+ @Override
+ public void onComplete(int result) {
+ }
+ });
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(mPlaybackLogicalAddress,
+ ADDR_TV);
+ HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
+ mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
+ HdmiCecMessage systemAudioModeRequest = HdmiCecMessageBuilder.buildSystemAudioModeRequest(
+ mPlaybackLogicalAddress, ADDR_AUDIO_SYSTEM, mPlaybackPhysicalAddress, true);
+ assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
+ assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+ assertThat(mNativeWrapper.getResultMessages()).contains(systemAudioModeRequest);
+ }
+
+ @Test
+ public void oneTouchPlay_PowerControlModeBroadcast() {
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
@@ -1502,7 +1574,7 @@
}
@Test
- public void oneTouchPlay_SendStandbyOnSleepNone() {
+ public void oneTouchPlay_PowerControlModeNone() {
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
HdmiControlManager.POWER_CONTROL_MODE_NONE);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
index 6880302..0578276 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
@@ -129,7 +129,7 @@
Context context = InstrumentationRegistry.getTargetContext();
mHdmiControlService =
- new HdmiControlService(context) {
+ new HdmiControlService(context, Collections.emptyList()) {
@Override
boolean isControlEnabled() {
return isControlEnabled;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index 59711a6..5a2f7bb 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -56,6 +56,7 @@
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Collections;
@SmallTest
@RunWith(JUnit4.class)
@@ -88,7 +89,8 @@
mMyLooper = mTestLooper.getLooper();
mHdmiControlService =
- new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
+ new HdmiControlService(InstrumentationRegistry.getTargetContext(),
+ Collections.emptyList()) {
@Override
void wakeUp() {
mWokenUp = true;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
index 2307a85..ebd8d77 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
@@ -37,6 +37,8 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.Collections;
+
/** Tests for {@link com.android.server.hdmi.HdmiCecMessageValidator} class. */
@SmallTest
@Presubmit
@@ -49,7 +51,7 @@
@Before
public void setUp() throws Exception {
HdmiControlService mHdmiControlService = new HdmiControlService(
- InstrumentationRegistry.getTargetContext());
+ InstrumentationRegistry.getTargetContext(), Collections.emptyList());
mHdmiControlService.setIoLooper(mTestLooper.getLooper());
mHdmiCecMessageValidator = new HdmiCecMessageValidator(mHdmiControlService);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
index b1998f5..e7d1b95 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
@@ -64,7 +64,7 @@
@Before
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getTargetContext();
- mHdmiControlService = new HdmiControlService(mContext) {
+ mHdmiControlService = new HdmiControlService(mContext, Collections.emptyList()) {
@Override
void invokeDeviceEventListeners(HdmiDeviceInfo device, int status) {
mDeviceEventListenerStatuses.add(status);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
index 572ffd9..dc633a2 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
@@ -47,6 +47,7 @@
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Collections;
@SmallTest
@Presubmit
@@ -83,7 +84,7 @@
mIThermalServiceMock, new Handler(myLooper)));
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
- mHdmiControlService = new HdmiControlService(contextSpy) {
+ mHdmiControlService = new HdmiControlService(contextSpy, Collections.emptyList()) {
@Override
boolean isControlEnabled() {
return true;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 0cf212c..3e1f8d9 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -62,6 +62,7 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Optional;
/**
@@ -204,7 +205,7 @@
HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(mContextSpy);
- mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy));
+ mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy, Collections.emptyList()));
doNothing().when(mHdmiControlServiceSpy)
.writeStringSystemProperty(anyString(), anyString());
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
index b820df3..9466f52 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
@@ -91,8 +91,6 @@
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
mHdmiCecConfig = new FakeHdmiCecConfig(mContextSpy);
- setHdmiControlEnabled(hdmiControlEnabled);
-
when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
new PowerManager(mContextSpy, mIPowerManagerMock,
mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
@@ -137,6 +135,7 @@
Looper looper = mTestLooper.getLooper();
mHdmiControlService.setIoLooper(looper);
mHdmiControlService.setHdmiCecConfig(mHdmiCecConfig);
+ setHdmiControlEnabled(hdmiControlEnabled);
mNativeWrapper = new FakeNativeWrapper();
HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper(
this.mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java
new file mode 100644
index 0000000..bd307fd
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
+import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
+import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
+import static com.android.server.hdmi.Constants.ADDR_TUNER_1;
+import static com.android.server.hdmi.Constants.ADDR_TV;
+import static com.android.server.hdmi.Constants.ADDR_UNREGISTERED;
+import static com.android.server.hdmi.Constants.MESSAGE_ACTIVE_SOURCE;
+import static com.android.server.hdmi.Constants.MESSAGE_ROUTING_INFORMATION;
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
+import static com.android.server.hdmi.RoutingControlAction.STATE_WAIT_FOR_ROUTING_INFORMATION;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.hdmi.HdmiPortInfo;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.os.Handler;
+import android.os.IPowerManager;
+import android.os.IThermalService;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.test.TestLooper;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.hdmi.HdmiCecFeatureAction.ActionTimer;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+@SmallTest
+@RunWith(JUnit4.class)
+public class RoutingControlActionTest {
+ /*
+ * Example connection diagram used in tests. Double-lined paths indicate the currently active
+ * routes.
+ *
+ *
+ * +-----------+
+ * | TV |
+ * | 0.0.0.0 |
+ * +---+-----+-+
+ * | |
+ * <----------+ 1) AVR -> Switch
+ * +----------+ | | +-----------+
+ * | AVR +---------+ +--+ Switch |
+ * | 1.0.0.0 | | 2.0.0.0 |
+ * +--+---++--+ +--++-----+-+ <-------+ 2) Recorder -> Blu-ray
+ * | || || |
+ * | || || +--------+
+ * +-----------+ | || +----------+ +----++----+ |
+ * | XBox +--+ ++--+ Tuner | | Blueray | +-----+----+
+ * | 1.1.0.0 | | 1.2.0.0 | | 2.1.0.0 | | Recorder |
+ * +-----------+ +----++----+ +----------+ | 2.2.0.0 |
+ * || +----------+
+ * ||
+ * +----++----+
+ * | Player |
+ * | 1.2.1.0 |
+ * +----------+
+ *
+ */
+
+ private static final int PHYSICAL_ADDRESS_TV = 0x0000;
+ private static final int PHYSICAL_ADDRESS_AVR = 0x1000;
+ private static final int PHYSICAL_ADDRESS_SWITCH = 0x2000;
+ private static final int PHYSICAL_ADDRESS_TUNER = 0x1200;
+ private static final int PHYSICAL_ADDRESS_PLAYER = 0x1210;
+ private static final int PHYSICAL_ADDRESS_BLUERAY = 0x2100;
+ private static final int PHYSICAL_ADDRESS_RECORDER = 0x2200;
+ private static final int PORT_1 = 1;
+ private static final int PORT_2 = 2;
+ private static final int VENDOR_ID_AVR = 0x11233;
+
+ private static final byte[] TUNER_PARAM =
+ new byte[] {(PHYSICAL_ADDRESS_TUNER >> 8) & 0xFF, PHYSICAL_ADDRESS_TUNER & 0xFF};
+ private static final byte[] PLAYER_PARAM =
+ new byte[] {(PHYSICAL_ADDRESS_PLAYER >> 8) & 0xFF, PHYSICAL_ADDRESS_PLAYER & 0xFF};
+
+ private static final HdmiDeviceInfo DEVICE_INFO_AVR =
+ new HdmiDeviceInfo(ADDR_AUDIO_SYSTEM, PHYSICAL_ADDRESS_AVR, PORT_1,
+ HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM, VENDOR_ID_AVR, "Audio");
+ private static final HdmiDeviceInfo DEVICE_INFO_PLAYER =
+ new HdmiDeviceInfo(ADDR_PLAYBACK_1, PHYSICAL_ADDRESS_PLAYER, PORT_1,
+ HdmiDeviceInfo.DEVICE_PLAYBACK, VENDOR_ID_AVR, "Player");
+ private static final HdmiCecMessage ROUTING_INFORMATION_TUNER = new HdmiCecMessage(
+ ADDR_UNREGISTERED, ADDR_BROADCAST, MESSAGE_ROUTING_INFORMATION, TUNER_PARAM);
+ private static final HdmiCecMessage ROUTING_INFORMATION_PLAYER = new HdmiCecMessage(
+ ADDR_UNREGISTERED, ADDR_BROADCAST, MESSAGE_ROUTING_INFORMATION, PLAYER_PARAM);
+ private static final HdmiCecMessage ACTIVE_SOURCE_TUNER = new HdmiCecMessage(
+ ADDR_TUNER_1, ADDR_BROADCAST, MESSAGE_ACTIVE_SOURCE, TUNER_PARAM);
+ private static final HdmiCecMessage ACTIVE_SOURCE_PLAYER = new HdmiCecMessage(
+ ADDR_PLAYBACK_1, ADDR_BROADCAST, MESSAGE_ACTIVE_SOURCE, PLAYER_PARAM);
+
+ private HdmiControlService mHdmiControlService;
+ private HdmiCecController mHdmiCecController;
+ private HdmiCecLocalDeviceTv mHdmiCecLocalDeviceTv;
+ private FakeNativeWrapper mNativeWrapper;
+ private Looper mMyLooper;
+ private TestLooper mTestLooper = new TestLooper();
+ private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+
+ @Mock
+ private IPowerManager mIPowerManagerMock;
+ @Mock
+ private IThermalService mIThermalServiceMock;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ Context context = InstrumentationRegistry.getTargetContext();
+ mMyLooper = mTestLooper.getLooper();
+ PowerManager powerManager = new PowerManager(context, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mMyLooper));
+
+ HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(context);
+
+ mHdmiControlService =
+ new HdmiControlService(InstrumentationRegistry.getTargetContext(),
+ Collections.emptyList()) {
+ @Override
+ boolean isControlEnabled() {
+ return true;
+ }
+
+ @Override
+ void wakeUp() {
+ }
+
+ @Override
+ protected void writeStringSystemProperty(String key, String value) {
+ // do nothing
+ }
+
+ @Override
+ boolean isPowerStandbyOrTransient() {
+ return false;
+ }
+
+ @Override
+ protected PowerManager getPowerManager() {
+ return powerManager;
+ }
+
+ @Override
+ protected HdmiCecConfig getHdmiCecConfig() {
+ return hdmiCecConfig;
+ }
+ };
+
+ mHdmiCecLocalDeviceTv = new HdmiCecLocalDeviceTv(mHdmiControlService);
+ mHdmiCecLocalDeviceTv.init();
+ mHdmiControlService.setIoLooper(mMyLooper);
+ mNativeWrapper = new FakeNativeWrapper();
+ mHdmiCecController = HdmiCecController.createWithNativeWrapper(
+ mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
+ mHdmiControlService.setCecController(mHdmiCecController);
+ mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
+ mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
+ mLocalDevices.add(mHdmiCecLocalDeviceTv);
+ HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
+ hdmiPortInfos[0] =
+ new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, PHYSICAL_ADDRESS_AVR,
+ true, false, false);
+ mNativeWrapper.setPortInfo(hdmiPortInfos);
+ mHdmiControlService.initService();
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mNativeWrapper.setPhysicalAddress(0x0000);
+ mTestLooper.dispatchAll();
+ mNativeWrapper.clearResultMessages();
+ mHdmiControlService.getHdmiCecNetwork().addCecDevice(DEVICE_INFO_AVR);
+ }
+
+ private static class TestActionTimer implements ActionTimer {
+ private int mState;
+
+ @Override
+ public void sendTimerMessage(int state, long delayMillis) {
+ mState = state;
+ }
+
+ @Override
+ public void clearTimerMessage() {
+ }
+
+ private int getState() {
+ return mState;
+ }
+ }
+
+ private static class TestInputSelectCallback extends IHdmiControlCallback.Stub {
+ private final List<Integer> mCallbackResult = new ArrayList<Integer>();
+
+ @Override
+ public void onComplete(int result) {
+ mCallbackResult.add(result);
+ }
+
+ private int getResult() {
+ assert (mCallbackResult.size() == 1);
+ return mCallbackResult.get(0);
+ }
+ }
+
+ private static RoutingControlAction createRoutingControlAction(HdmiCecLocalDeviceTv localDevice,
+ TestInputSelectCallback callback) {
+ return new RoutingControlAction(localDevice, PHYSICAL_ADDRESS_AVR, callback);
+ }
+
+ // Routing control succeeds against the device connected directly to the port. Action
+ // won't get any <Routing Information> in this case. It times out on <Routing Information>,
+ // regards the directly connected one as the new routing path to switch to.
+ @Test
+ public void testRoutingControl_succeedForDirectlyConnectedDevice() {
+ TestInputSelectCallback callback = new TestInputSelectCallback();
+ TestActionTimer actionTimer = new TestActionTimer();
+ mHdmiControlService.getHdmiCecNetwork().addCecDevice(DEVICE_INFO_AVR);
+
+ RoutingControlAction action = createRoutingControlAction(mHdmiCecLocalDeviceTv, callback);
+ action.setActionTimer(actionTimer);
+ action.start();
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_ROUTING_INFORMATION);
+
+ action.handleTimerEvent(actionTimer.getState());
+ mTestLooper.dispatchAll();
+ HdmiCecMessage setStreamPath = HdmiCecMessageBuilder.buildSetStreamPath(
+ ADDR_TV, PHYSICAL_ADDRESS_AVR);
+ assertThat(mNativeWrapper.getResultMessages()).contains(setStreamPath);
+ }
+
+ // Succeeds by receiving a couple of <Routing Information> commands, followed by
+ // <Set Stream Path> going out in the end.
+ @Test
+ public void testRoutingControl_succeedForDeviceBehindSwitch() {
+ TestInputSelectCallback callback = new TestInputSelectCallback();
+ TestActionTimer actionTimer = new TestActionTimer();
+ mHdmiControlService.getHdmiCecNetwork().addCecDevice(DEVICE_INFO_PLAYER);
+ RoutingControlAction action = createRoutingControlAction(mHdmiCecLocalDeviceTv, callback);
+ action.setActionTimer(actionTimer);
+ action.start();
+
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_ROUTING_INFORMATION);
+
+ action.processCommand(ROUTING_INFORMATION_TUNER);
+ action.processCommand(ROUTING_INFORMATION_PLAYER);
+
+ action.handleTimerEvent(actionTimer.getState());
+ mTestLooper.dispatchAll();
+ HdmiCecMessage setStreamPath = HdmiCecMessageBuilder.buildSetStreamPath(
+ ADDR_TV, PHYSICAL_ADDRESS_PLAYER);
+ assertThat(mNativeWrapper.getResultMessages()).contains(setStreamPath);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
index 2cf4ef1..290e4b0 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
@@ -48,6 +48,7 @@
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Collections;
/**
* Test for {@link SystemAudioAutoInitiationAction}.
@@ -86,7 +87,7 @@
mIThermalServiceMock, new Handler(myLooper)));
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
- mHdmiControlService = new HdmiControlService(mContextSpy) {
+ mHdmiControlService = new HdmiControlService(mContextSpy, Collections.emptyList()) {
@Override
AudioManager getAudioManager() {
return new AudioManager() {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
index e82c788..2019a16 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
@@ -38,6 +38,8 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.Collections;
+
/** Tests for {@link SystemAudioInitiationActionFromAvr} */
@SmallTest
@Presubmit
@@ -65,7 +67,8 @@
Context context = InstrumentationRegistry.getTargetContext();
- HdmiControlService hdmiControlService = new HdmiControlService(context) {
+ HdmiControlService hdmiControlService = new HdmiControlService(context,
+ Collections.emptyList()) {
@Override
void sendCecCommand(
HdmiCecMessage command, @Nullable SendMessageCallback callback) {
diff --git a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
index 2efebbf..8eb3cf3 100644
--- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
@@ -4,7 +4,9 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyString;
@@ -102,6 +104,20 @@
}
@Test
+ public void testStringToIntArrayAndIntArrayToString() {
+ final int[] netCapabilitiesIntArray = { 1, 3, 5, 7, 9 };
+ final String netCapabilitiesStr = "1,3,5,7,9";
+ final String netCapabilitiesStrWithErrorInt = "1,3,a,7,9";
+ final String emptyString = "";
+ final String str1 = JobStore.intArrayToString(netCapabilitiesIntArray);
+ assertArrayEquals(netCapabilitiesIntArray, JobStore.stringToIntArray(str1));
+ assertEquals(0, JobStore.stringToIntArray(emptyString).length);
+ assertThrows(NumberFormatException.class,
+ () -> JobStore.stringToIntArray(netCapabilitiesStrWithErrorInt));
+ assertEquals(netCapabilitiesStr, JobStore.intArrayToString(netCapabilitiesIntArray));
+ }
+
+ @Test
public void testMaybeWriteStatusToDisk() throws Exception {
int taskId = 5;
long runByMillis = 20000L; // 20s
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 23517a9..70641c2 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -162,13 +162,13 @@
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.BroadcastInterceptingContext.FutureIntent;
+import com.android.internal.util.test.FsUtil;
import com.android.server.DeviceIdleInternal;
import com.android.server.LocalServices;
import com.android.server.usage.AppStandbyInternal;
import com.google.common.util.concurrent.AbstractFuture;
-import libcore.io.IoUtils;
import libcore.io.Streams;
import org.junit.After;
@@ -2385,7 +2385,7 @@
private void setNetpolicyXml(Context context) throws Exception {
mPolicyDir = context.getFilesDir();
if (mPolicyDir.exists()) {
- IoUtils.deleteContents(mPolicyDir);
+ FsUtil.deleteContents(mPolicyDir);
}
if (!TextUtils.isEmpty(mNetpolicyXml)) {
final String assetPath = NETPOLICY_DIR + "/" + mNetpolicyXml;
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index 67dd055..be942d6 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -30,9 +30,8 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.SigningDetails;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.UserInfo;
import android.content.pm.parsing.ParsingPackage;
import android.content.pm.parsing.component.ParsedActivity;
@@ -252,8 +251,8 @@
final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
watcher.register();
final Signature frameworkSignature = Mockito.mock(Signature.class);
- final PackageParser.SigningDetails frameworkSigningDetails =
- new PackageParser.SigningDetails(new Signature[]{frameworkSignature}, 1);
+ final SigningDetails frameworkSigningDetails =
+ new SigningDetails(new Signature[]{frameworkSignature}, 1);
final ParsingPackage android = pkg("android");
watcher.verifyNoChangeReported("prepare");
android.addProtectedBroadcast("TEST_ACTION");
@@ -592,12 +591,12 @@
appsFilter.onSystemReady();
final Signature frameworkSignature = Mockito.mock(Signature.class);
- final PackageParser.SigningDetails frameworkSigningDetails =
- new PackageParser.SigningDetails(new Signature[]{frameworkSignature}, 1);
+ final SigningDetails frameworkSigningDetails =
+ new SigningDetails(new Signature[]{frameworkSignature}, 1);
final Signature otherSignature = Mockito.mock(Signature.class);
- final PackageParser.SigningDetails otherSigningDetails =
- new PackageParser.SigningDetails(new Signature[]{otherSignature}, 1);
+ final SigningDetails otherSigningDetails =
+ new SigningDetails(new Signature[]{otherSignature}, 1);
simulateAddPackage(appsFilter, pkg("android"), 1000,
b -> b.setSigningDetails(frameworkSigningDetails));
@@ -1199,8 +1198,8 @@
private void simulateAddBasicAndroid(AppsFilter appsFilter) throws Exception {
final Signature frameworkSignature = Mockito.mock(Signature.class);
- final PackageParser.SigningDetails frameworkSigningDetails =
- new PackageParser.SigningDetails(new Signature[]{frameworkSignature}, 1);
+ final SigningDetails frameworkSigningDetails =
+ new SigningDetails(new Signature[]{frameworkSignature}, 1);
final ParsingPackage android = pkg("android");
simulateAddPackage(appsFilter, android, 1000,
b -> b.setSigningDetails(frameworkSigningDetails));
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 7df2dd6..d470e1e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -77,12 +77,12 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.content.pm.ShortcutServiceInternal;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.SigningInfo;
import android.content.pm.UserInfo;
import android.content.res.Resources;
@@ -1413,9 +1413,9 @@
pi.applicationInfo.setVersionCode(version);
pi.signatures = null;
pi.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
genSignatures(signatures),
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
return pi;
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index 128cbaa..a710839 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -15,6 +15,10 @@
*/
package com.android.server.pm;
+import static android.content.pm.permission.CompatibilityPermissionInfo.COMPAT_PERMS;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -25,6 +29,7 @@
import static java.lang.Boolean.TRUE;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
+import static java.util.stream.Collectors.toList;
import android.annotation.NonNull;
import android.content.Context;
@@ -35,10 +40,10 @@
import android.content.pm.FeatureInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.Property;
-import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
import android.content.pm.ServiceInfo;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.parsing.ParsingPackage;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedComponent;
@@ -83,6 +88,7 @@
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
@@ -106,6 +112,7 @@
private static final String TEST_APP2_APK = "PackageParserTestApp2.apk";
private static final String TEST_APP3_APK = "PackageParserTestApp3.apk";
private static final String TEST_APP4_APK = "PackageParserTestApp4.apk";
+ private static final String TEST_APP5_APK = "PackageParserTestApp5.apk";
private static final String PACKAGE_NAME = "com.android.servicestests.apps.packageparserapp";
@Before
@@ -506,6 +513,56 @@
}
}
+ @Test
+ public void testParseModernPackageHasNoCompatPermissions() throws Exception {
+ final File testFile = extractFile(TEST_APP1_APK);
+ try {
+ final ParsedPackage pkg = new TestPackageParser2()
+ .parsePackage(testFile, 0 /*flags*/, false /*useCaches*/);
+ final List<String> compatPermissions =
+ Arrays.stream(COMPAT_PERMS).map(p -> p.name).collect(toList());
+ assertWithMessage(
+ "Compatibility permissions shouldn't be added into uses permissions.")
+ .that(pkg.getUsesPermissions().stream().map(p -> p.name).collect(toList()))
+ .containsNoneIn(compatPermissions);
+ assertWithMessage(
+ "Compatibility permissions shouldn't be added into requested permissions.")
+ .that(pkg.getRequestedPermissions()).containsNoneIn(compatPermissions);
+ assertWithMessage(
+ "Compatibility permissions shouldn't be added into implicit permissions.")
+ .that(pkg.getImplicitPermissions()).containsNoneIn(compatPermissions);
+ } finally {
+ testFile.delete();
+ }
+ }
+
+ @Test
+ public void testParseLegacyPackageHasCompatPermissions() throws Exception {
+ final File testFile = extractFile(TEST_APP5_APK);
+ try {
+ final ParsedPackage pkg = new TestPackageParser2()
+ .parsePackage(testFile, 0 /*flags*/, false /*useCaches*/);
+ assertWithMessage(
+ "Compatibility permissions should be added into uses permissions.")
+ .that(Arrays.stream(COMPAT_PERMS).map(p -> p.name)
+ .allMatch(pkg.getUsesPermissions().stream().map(p -> p.name)
+ .collect(toList())::contains))
+ .isTrue();
+ assertWithMessage(
+ "Compatibility permissions should be added into requested permissions.")
+ .that(Arrays.stream(COMPAT_PERMS).map(p -> p.name)
+ .allMatch(pkg.getRequestedPermissions()::contains))
+ .isTrue();
+ assertWithMessage(
+ "Compatibility permissions should be added into implicit permissions.")
+ .that(Arrays.stream(COMPAT_PERMS).map(p -> p.name)
+ .allMatch(pkg.getImplicitPermissions()::contains))
+ .isTrue();
+ } finally {
+ testFile.delete();
+ }
+ }
+
/**
* A trivial subclass of package parser that only caches the package name, and throws away
* all other information.
@@ -645,7 +702,8 @@
assertBundleApproximateEquals(a.getMetaData(), b.getMetaData());
assertEquals(a.getVersionName(), b.getVersionName());
assertEquals(a.getSharedUserId(), b.getSharedUserId());
- assertArrayEquals(a.getSigningDetails().signatures, b.getSigningDetails().signatures);
+ assertArrayEquals(a.getSigningDetails().getSignatures(),
+ b.getSigningDetails().getSignatures());
assertEquals(a.getRestrictedAccountType(), b.getRestrictedAccountType());
assertEquals(a.getRequiredAccountType(), b.getRequiredAccountType());
assertEquals(a.getOverlayTarget(), b.getOverlayTarget());
@@ -653,7 +711,7 @@
assertEquals(a.getOverlayCategory(), b.getOverlayCategory());
assertEquals(a.getOverlayPriority(), b.getOverlayPriority());
assertEquals(a.isOverlayIsStatic(), b.isOverlayIsStatic());
- assertEquals(a.getSigningDetails().publicKeys, b.getSigningDetails().publicKeys);
+ assertEquals(a.getSigningDetails().getPublicKeys(), b.getSigningDetails().getPublicKeys());
assertEquals(a.getUpgradeKeySets(), b.getUpgradeKeySets());
assertEquals(a.getKeySetMapping(), b.getKeySetMapping());
assertArrayEquals(a.getRestrictUpdateHash(), b.getRestrictUpdateHash());
@@ -873,9 +931,9 @@
.setVersionName("foo17")
.setSharedUserId("foo18")
.setSigningDetails(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[]{new Signature(new byte[16])},
- 2,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2,
new ArraySet<>(),
null)
)
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
index f75751b..f551ad1 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
@@ -16,8 +16,8 @@
package com.android.server.pm;
-import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
+import android.content.pm.SigningDetails;
import android.util.ArraySet;
import android.util.SparseArray;
@@ -47,7 +47,7 @@
private String[] mUsesStaticLibraries;
private long[] mUsesStaticLibrariesVersions;
private Map<String, ArraySet<String>> mMimeGroups;
- private PackageParser.SigningDetails mSigningDetails;
+ private SigningDetails mSigningDetails;
private UUID mDomainSetId = UUID.randomUUID();
public PackageSettingBuilder setPackage(AndroidPackage pkg) {
@@ -159,7 +159,7 @@
}
public PackageSettingBuilder setSigningDetails(
- PackageParser.SigningDetails signingDetails) {
+ SigningDetails signingDetails) {
mSigningDetails = signingDetails;
return this;
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
index 27f3eec..b9431bf 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
@@ -22,11 +22,9 @@
import static org.junit.Assert.fail;
import android.content.Context;
-import android.content.pm.PackageParser;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.util.TypedXmlPullParser;
-import android.util.TypedXmlPullParser;
-import android.util.TypedXmlSerializer;
import android.util.Xml;
import androidx.test.InstrumentationRegistry;
@@ -39,7 +37,6 @@
import org.junit.runner.RunWith;
import org.xmlpull.v1.XmlPullParser;
-import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
@@ -107,10 +104,10 @@
}
private static final int[] CAPABILITIES =
- {PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA,
- PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID,
- PackageParser.SigningDetails.CertCapabilities.PERMISSION,
- PackageParser.SigningDetails.CertCapabilities.ROLLBACK};
+ {SigningDetails.CertCapabilities.INSTALLED_DATA,
+ SigningDetails.CertCapabilities.SHARED_USER_ID,
+ SigningDetails.CertCapabilities.PERMISSION,
+ SigningDetails.CertCapabilities.ROLLBACK};
@Before
public void setUp() throws Exception {
@@ -173,7 +170,7 @@
assertEquals(
"The signing details was not UNKNOWN after parsing an invalid public key cert key"
+ " attribute",
- PackageParser.SigningDetails.UNKNOWN, mPackageSetting.signatures.mSigningDetails);
+ SigningDetails.UNKNOWN, mPackageSetting.signatures.mSigningDetails);
}
@Test
@@ -181,14 +178,14 @@
// Verifies if the sigs count attribute is missing then the signature cannot be read but the
// method does not throw an exception.
verifyReadXmlReturnsExpectedSignatures("xml/one-signer-missing-sigs-count.xml",
- PackageParser.SigningDetails.SignatureSchemeVersion.UNKNOWN);
+ SigningDetails.SignatureSchemeVersion.UNKNOWN);
}
@Test
public void testReadXmlWithMissingSchemeVersion() throws Exception {
// Verifies if the schemeVersion is an invalid value the signature can still be obtained.
verifyReadXmlReturnsExpectedSignatures("xml/one-signer-missing-scheme-version.xml",
- PackageParser.SigningDetails.SignatureSchemeVersion.UNKNOWN,
+ SigningDetails.SignatureSchemeVersion.UNKNOWN,
FIRST_EXPECTED_SIGNATURE);
}
@@ -198,7 +195,7 @@
// obtained.
verifyReadXmlReturnsExpectedSignaturesAndLineage(
"xml/three-signers-in-lineage-missing-scheme-version.xml",
- PackageParser.SigningDetails.SignatureSchemeVersion.UNKNOWN,
+ SigningDetails.SignatureSchemeVersion.UNKNOWN,
FIRST_EXPECTED_SIGNATURE, SECOND_EXPECTED_SIGNATURE, THIRD_EXPECTED_SIGNATURE);
}
@@ -386,7 +383,7 @@
verifySignaturesContainExpectedValues(signatures, expectedSignatures);
assertEquals("The returned signature scheme is not the expected value",
expectedSchemeVersion,
- mPackageSetting.signatures.mSigningDetails.signatureSchemeVersion);
+ mPackageSetting.signatures.mSigningDetails.getSignatureSchemeVersion());
}
/**
@@ -402,7 +399,7 @@
Set<String> expectedSignatures = createSetOfSignatures(expectedSignatureValues);
verifySignaturesContainExpectedValues(signatures, expectedSignatures);
assertEquals("The returned signature scheme is not the expected value", schemeVersion,
- mPackageSetting.signatures.mSigningDetails.signatureSchemeVersion);
+ mPackageSetting.signatures.mSigningDetails.getSignatureSchemeVersion());
for (Signature signature : signatures) {
String signatureValue = HexDump.toHexString(signature.toByteArray(), false);
int expectedCapabilities = SIGNATURE_TO_CAPABILITY_MAP.get(signatureValue);
diff --git a/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
index f1930d7..cee4cda 100644
--- a/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
@@ -91,6 +91,16 @@
}
@Test
+ public void getSeInfoTargetingCurDevelopment() {
+ AndroidPackage pkg = makePackage(Build.VERSION_CODES.CUR_DEVELOPMENT);
+ when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_LATEST_CHANGES),
+ argThat(argument -> argument.packageName.equals(pkg.getPackageName()))))
+ .thenReturn(true);
+ assertThat(SELinuxMMAC.getSeInfo(pkg, null, mMockCompatibility),
+ is("default:targetSdkVersion=" + Build.VERSION_CODES.CUR_DEVELOPMENT));
+ }
+
+ @Test
public void getSeInfoNoOptInButAlreadyR() {
AndroidPackage pkg = makePackage(R_OPT_IN_VERSION);
when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_R_CHANGES),
diff --git a/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java
index 182760b..b447857 100644
--- a/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java
@@ -25,8 +25,8 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.SigningInfo;
import android.platform.test.annotations.Presubmit;
import android.test.MoreAsserts;
@@ -95,9 +95,9 @@
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -114,9 +114,9 @@
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -197,9 +197,9 @@
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -219,9 +219,9 @@
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -240,9 +240,9 @@
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1, SIGNATURE_2},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -262,9 +262,9 @@
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -285,9 +285,9 @@
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -309,9 +309,9 @@
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1, SIGNATURE_2},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -336,9 +336,9 @@
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1, SIGNATURE_2},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
diff --git a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
index 4618157..2a4c3fd 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
@@ -32,6 +32,7 @@
import android.content.pm.VersionedPackage;
import android.content.rollback.PackageRollbackInfo;
import android.content.rollback.PackageRollbackInfo.RestoreInfo;
+import android.util.SparseIntArray;
import com.android.server.pm.ApexManager;
import com.android.server.pm.Installer;
@@ -119,8 +120,9 @@
}
private static Rollback createRollbackForId(int rollbackId) {
- return new Rollback(rollbackId, new File("/does/not/exist"), -1,
- 0, "com.xyz");
+ return new Rollback(rollbackId, new File("/does/not/exist"), -1, /* isStaged */ false, 0,
+ "com.xyz", null, new SparseIntArray(0));
+
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
index c42f936..9d56a36 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
@@ -82,7 +82,7 @@
+ "'ceSnapshotInodes':[]}],'isStaged':false,'causePackages':[{'packageName':'hello',"
+ "'longVersionCode':23},{'packageName':'something','longVersionCode':999}],"
+ "'committedSessionId':45654465},'timestamp':'2019-10-01T12:29:08.855Z',"
- + "'stagedSessionId':-1,'state':'enabling','apkSessionId':-1,"
+ + "'originalSessionId':567,'state':'enabling','apkSessionId':-1,"
+ "'restoreUserDataInProgress':true, 'userId':0,"
+ "'installerPackageName':'some.installer'}";
@@ -102,7 +102,7 @@
+ "'ceSnapshotInodes':[]}],'isStaged':false,'causePackages':[{'packageName':'hello',"
+ "'longVersionCode':23},{'packageName':'something','longVersionCode':999}],"
+ "'committedSessionId':45654465},'timestamp':'2019-10-01T12:29:08.855Z',"
- + "'stagedSessionId':-1,'state':'enabling','apkSessionId':-1,"
+ + "'originalSessionId':567,'state':'enabling','apkSessionId':-1,"
+ "'restoreUserDataInProgress':true, 'userId':0,"
+ "'installerPackageName':'some.installer',"
+ "'extensionVersions':[{'sdkVersion':5,'extensionVersion':25},"
@@ -129,12 +129,13 @@
SparseIntArray extensionVersions = new SparseIntArray();
extensionVersions.put(30, 71);
Rollback rollback = mRollbackStore.createNonStagedRollback(
- ID, USER, INSTALLER, null, extensionVersions);
+ ID, 567, USER, INSTALLER, null, extensionVersions);
assertThat(rollback.getBackupDir().getAbsolutePath())
.isEqualTo(mFolder.getRoot().getAbsolutePath() + "/" + ID);
assertThat(rollback.isStaged()).isFalse();
+ assertThat(rollback.getOriginalSessionId()).isEqualTo(567);
assertThat(rollback.info.getRollbackId()).isEqualTo(ID);
assertThat(rollback.info.getPackages()).isEmpty();
assertThat(rollback.isEnabling()).isTrue();
@@ -153,7 +154,7 @@
.isEqualTo(mFolder.getRoot().getAbsolutePath() + "/" + ID);
assertThat(rollback.isStaged()).isTrue();
- assertThat(rollback.getStagedSessionId()).isEqualTo(897);
+ assertThat(rollback.getOriginalSessionId()).isEqualTo(897);
assertThat(rollback.info.getRollbackId()).isEqualTo(ID);
assertThat(rollback.info.getPackages()).isEmpty();
@@ -168,7 +169,7 @@
extensionVersions.put(5, 25);
extensionVersions.put(30, 71);
Rollback origRb = mRollbackStore.createNonStagedRollback(
- ID, USER, INSTALLER, null, extensionVersions);
+ ID, 567, USER, INSTALLER, null, extensionVersions);
origRb.setRestoreUserDataInProgress(true);
origRb.info.getCausePackages().add(new VersionedPackage("com.made.up", 2));
@@ -218,7 +219,7 @@
@Test
public void loadFromJsonNoExtensionVersions() throws Exception {
Rollback expectedRb = mRollbackStore.createNonStagedRollback(
- ID, USER, INSTALLER, null, new SparseIntArray(0));
+ ID, 567, USER, INSTALLER, null, new SparseIntArray(0));
expectedRb.setTimestamp(Instant.parse("2019-10-01T12:29:08.855Z"));
expectedRb.setRestoreUserDataInProgress(true);
@@ -268,7 +269,7 @@
extensionVersions.put(5, 25);
extensionVersions.put(30, 71);
Rollback expectedRb = mRollbackStore.createNonStagedRollback(
- ID, USER, INSTALLER, null, extensionVersions);
+ ID, 567, USER, INSTALLER, null, extensionVersions);
expectedRb.setTimestamp(Instant.parse("2019-10-01T12:29:08.855Z"));
expectedRb.setRestoreUserDataInProgress(true);
@@ -315,7 +316,7 @@
@Test
public void saveAndDelete() {
Rollback rollback = mRollbackStore.createNonStagedRollback(
- ID, USER, INSTALLER, null, new SparseIntArray(0));
+ ID, 567, USER, INSTALLER, null, new SparseIntArray(0));
RollbackStore.saveRollback(rollback);
@@ -331,7 +332,7 @@
@Test
public void saveToHistoryAndLoad() {
Rollback origRb = mRollbackStore.createNonStagedRollback(
- ID, USER, INSTALLER, null, new SparseIntArray(0));
+ ID, 567, USER, INSTALLER, null, new SparseIntArray(0));
mRollbackStore.saveRollbackToHistory(origRb);
List<Rollback> loadedRollbacks = mRollbackStore.loadHistorialRollbacks();
@@ -364,7 +365,7 @@
assertThat(b.getApexPackageNames())
.containsExactlyElementsIn(a.getApexPackageNames());
- assertThat(b.getStagedSessionId()).isEqualTo(a.getStagedSessionId());
+ assertThat(b.getOriginalSessionId()).isEqualTo(a.getOriginalSessionId());
assertThat(b.info.getCommittedSessionId()).isEqualTo(a.info.getCommittedSessionId());
diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
index cf1ed48..5ba4851 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
@@ -74,18 +74,28 @@
when(mMockPmi.getPackageList()).thenReturn(mPackageList);
}
+ private Rollback createStagedRollback(int rollbackId, File backupDir, int originalSessionId) {
+ return new Rollback(rollbackId, backupDir, originalSessionId, /* isStaged */ true, USER,
+ INSTALLER, null, new SparseIntArray(0));
+ }
+
+ private Rollback createNonStagedRollback(int rollbackId, File backupDir) {
+ return new Rollback(rollbackId, backupDir, -1, /* isStaged */ false, USER,
+ INSTALLER, null, new SparseIntArray(0));
+ }
+
@Test
public void newEmptyStagedRollbackDefaults() {
int rollbackId = 123;
int sessionId = 567;
File file = new File("/test/testing");
- Rollback rollback = new Rollback(rollbackId, file, sessionId, USER, INSTALLER);
+ Rollback rollback = createStagedRollback(rollbackId, file, sessionId);
assertThat(rollback.isEnabling()).isTrue();
assertThat(rollback.getBackupDir().getAbsolutePath()).isEqualTo("/test/testing");
assertThat(rollback.isStaged()).isTrue();
- assertThat(rollback.getStagedSessionId()).isEqualTo(567);
+ assertThat(rollback.getOriginalSessionId()).isEqualTo(567);
}
@Test
@@ -93,7 +103,7 @@
int rollbackId = 123;
File file = new File("/test/testing");
- Rollback rollback = new Rollback(rollbackId, file, -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(rollbackId, file);
assertThat(rollback.isEnabling()).isTrue();
assertThat(rollback.getBackupDir().getAbsolutePath()).isEqualTo("/test/testing");
@@ -102,8 +112,7 @@
@Test
public void rollbackMadeAvailable() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER,
- INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
assertThat(rollback.isEnabling()).isTrue();
assertThat(rollback.isAvailable()).isFalse();
@@ -121,7 +130,7 @@
@Test
public void deletedRollbackCannotBeMadeAvailable() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
rollback.delete(mMockDataHelper, "test");
@@ -135,7 +144,7 @@
@Test
public void getPackageNamesAllAndJustApex() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 11, true);
PackageRollbackInfo pkgInfo3 = newPkgInfoFor(PKG_3, 19, 1, false);
@@ -149,7 +158,7 @@
@Test
public void includesPackagesAfterEnable() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
PackageRollbackInfo pkgInfo3 = newPkgInfoFor(PKG_3, 157, 156, false);
@@ -177,7 +186,7 @@
@Test
public void snapshotWhenEnabling() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
@@ -195,7 +204,7 @@
@Test
public void snapshotWhenAvailable() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
@@ -216,7 +225,7 @@
@Test
public void snapshotWhenDeleted() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
@@ -237,7 +246,7 @@
@Test
public void snapshotThenDeleteNoApex() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, false);
rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
@@ -259,7 +268,7 @@
@Test
public void snapshotThenDeleteWithApex() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
@@ -282,7 +291,7 @@
@Test
public void restoreUserDataDoesNothingIfNotInProgress() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
@@ -297,7 +306,7 @@
@Test
public void restoreUserDataDoesNothingIfPackageNotFound() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
@@ -313,7 +322,7 @@
@Test
public void restoreUserDataRestoresIfInProgressAndPackageFound() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
@@ -329,20 +338,9 @@
}
@Test
- public void notifySessionWithSuccess() {
- int[] sessionIds = new int[]{ 7777, 8888 };
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER,
- sessionIds, new SparseIntArray(0));
- // The 1st invocation returns false because not all child sessions are notified.
- assertThat(rollback.notifySessionWithSuccess()).isFalse();
- // The 2nd invocation returns true because now all child sessions are notified.
- assertThat(rollback.notifySessionWithSuccess()).isTrue();
- }
-
- @Test
public void allPackagesEnabled() {
int[] sessionIds = new int[]{ 7777, 8888 };
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER,
+ Rollback rollback = new Rollback(123, new File("/test/testing"), -1, false, USER, INSTALLER,
sessionIds, new SparseIntArray(0));
// #allPackagesEnabled returns false when 1 out of 2 packages is enabled.
rollback.info.getPackages().add(newPkgInfoFor(PKG_1, 12, 10, false));
diff --git a/services/tests/servicestests/src/com/android/server/rotationresolver/RotationResolverManagerPerUserServiceTest.java b/services/tests/servicestests/src/com/android/server/rotationresolver/RotationResolverManagerPerUserServiceTest.java
index fa2123c..03ccf8c 100644
--- a/services/tests/servicestests/src/com/android/server/rotationresolver/RotationResolverManagerPerUserServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/rotationresolver/RotationResolverManagerPerUserServiceTest.java
@@ -76,7 +76,8 @@
// setup a spy for the RotationResolverManagerPerUserService.
final RotationResolverManagerService mainService = new RotationResolverManagerService(
mContext);
- mService = new RotationResolverManagerPerUserService(mainService, /* Lock */ new Object(),
+ final Object lock = new Object();
+ mService = new RotationResolverManagerPerUserService(mainService, lock,
mContext.getUserId());
mCancellationSignal = new CancellationSignal();
@@ -84,15 +85,13 @@
mRequest = new RotationResolutionRequest("", Surface.ROTATION_0, Surface.ROTATION_0,
true, 1000L);
this.mService.mCurrentRequest = new RemoteRotationResolverService.RotationRequest(
- mMockCallbackInternal, mRequest, mCancellationSignal);
+ mMockCallbackInternal, mRequest, mCancellationSignal, lock);
this.mService.getMaster().mIsServiceEnabled = true;
ComponentName componentName = new ComponentName(PACKAGE_NAME, CLASS_NAME);
this.mService.mRemoteService = new MockRemoteRotationResolverService(mContext,
- componentName, mContext.getUserId(),
- /* idleUnbindTimeoutMs */60000L,
- /* Lock */ new Object());
+ componentName, mContext.getUserId(), /* idleUnbindTimeoutMs */60000L);
}
@Test
@@ -126,13 +125,13 @@
}
static class MockRemoteRotationResolverService extends RemoteRotationResolverService {
- MockRemoteRotationResolverService(Context context, ComponentName serviceName,
- int userId, long idleUnbindTimeoutMs, Object lock) {
- super(context, serviceName, userId, idleUnbindTimeoutMs, lock);
+ MockRemoteRotationResolverService(Context context, ComponentName serviceName, int userId,
+ long idleUnbindTimeoutMs) {
+ super(context, serviceName, userId, idleUnbindTimeoutMs);
}
@Override
- public void resolveRotationLocked(RotationRequest request) {
+ public void resolveRotation(RotationRequest request) {
request.mCallbackInternal.onSuccess(request.mRemoteRequest.getProposedRotation());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java
new file mode 100644
index 0000000..1947481
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java
@@ -0,0 +1,1054 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.soundtrigger_middleware;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.atMost;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.Status;
+import android.os.HwParcel;
+import android.os.IBinder;
+import android.os.IHwBinder;
+import android.os.IHwInterface;
+import android.os.RemoteException;
+import android.system.OsConstants;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.mockito.ArgumentCaptor;
+
+import java.util.LinkedList;
+import java.util.List;
+
+@RunWith(Parameterized.class)
+public class SoundHw2CompatTest {
+ @Parameterized.Parameter(0) public String mVersion;
+ @Parameterized.Parameter(1) public boolean mSupportConcurrentCapture;
+
+ private final Runnable mRebootRunnable = mock(Runnable.class);
+ private ISoundTriggerHal mCanonical;
+ private CaptureStateNotifier mCaptureStateNotifier;
+ private android.hardware.soundtrigger.V2_0.ISoundTriggerHw mHalDriver;
+
+ // We run the test once for every version of the underlying driver.
+ @Parameterized.Parameters(name = "{0}, concurrent={1}")
+ public static Iterable<Object[]> data() {
+ List<Object[]> result = new LinkedList<>();
+
+ for (String version : new String[]{"V2_0", "V2_1", "V2_2", "V2_3", "V2_4",}) {
+ for (boolean concurrentCapture : new boolean[]{false, true}) {
+ result.add(new Object[]{version, concurrentCapture});
+ }
+ }
+
+ return result;
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mHalDriver = (android.hardware.soundtrigger.V2_0.ISoundTriggerHw) mock(Class.forName(
+ String.format("android.hardware.soundtrigger.%s.ISoundTriggerHw", mVersion)));
+
+ clearInvocations(mRebootRunnable);
+
+ // This binder is associated with the mock, so it can be cast to either version of the
+ // HAL interface.
+ final IHwBinder binder = new IHwBinder() {
+ @Override
+ public void transact(int code, HwParcel request, HwParcel reply, int flags)
+ throws RemoteException {
+ // This is a little hacky, but a very easy way to gracefully reject a request for
+ // an unsupported interface (after queryLocalInterface() returns null, the client
+ // will attempt a remote transaction to obtain the interface. RemoteException will
+ // cause it to give up).
+ throw new RemoteException();
+ }
+
+ @Override
+ public IHwInterface queryLocalInterface(String descriptor) {
+ if (descriptor.equals("android.hardware.soundtrigger@2.0::ISoundTriggerHw")
+ || descriptor.equals("android.hardware.soundtrigger@2.1::ISoundTriggerHw")
+ && mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw
+ || descriptor.equals("android.hardware.soundtrigger@2.2::ISoundTriggerHw")
+ && mHalDriver instanceof android.hardware.soundtrigger.V2_2.ISoundTriggerHw
+ || descriptor.equals("android.hardware.soundtrigger@2.3::ISoundTriggerHw")
+ && mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw
+ || descriptor.equals("android.hardware.soundtrigger@2.4::ISoundTriggerHw")
+ && mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
+ return mHalDriver;
+ }
+ return null;
+ }
+
+ @Override
+ public boolean linkToDeath(DeathRecipient recipient, long cookie) {
+ try {
+ return mHalDriver.linkToDeath(recipient, cookie);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public boolean unlinkToDeath(DeathRecipient recipient) {
+ try {
+ return mHalDriver.unlinkToDeath(recipient);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+ };
+ when(mHalDriver.asBinder()).thenReturn(binder);
+
+ android.hardware.soundtrigger.V2_3.Properties halProperties =
+ TestUtil.createDefaultProperties_2_3(mSupportConcurrentCapture);
+ doAnswer(invocation -> {
+ ((android.hardware.soundtrigger.V2_0.ISoundTriggerHw.getPropertiesCallback) invocation.getArgument(
+ 0)).onValues(0, halProperties.base);
+ return null;
+ }).when(mHalDriver).getProperties(any());
+
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
+ android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
+ (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
+ doAnswer(invocation -> {
+ ((android.hardware.soundtrigger.V2_3.ISoundTriggerHw.getProperties_2_3Callback) invocation.getArgument(
+ 0)).onValues(0, halProperties);
+ return null;
+ }).when(driver).getProperties_2_3(any());
+ }
+
+ mCaptureStateNotifier = spy(new CaptureStateNotifier());
+
+ mCanonical = SoundTriggerHw2Compat.create(mHalDriver, mRebootRunnable,
+ mCaptureStateNotifier);
+
+ // During initialization any method can be called, but after we're starting to enforce that
+ // no additional methods are called.
+ clearInvocations(mHalDriver);
+ }
+
+ @After
+ public void tearDown() {
+ mCanonical.detach();
+ verifyNoMoreInteractions(mHalDriver);
+ verifyNoMoreInteractions(mRebootRunnable);
+ mCaptureStateNotifier.verifyNoMoreListeners();
+ }
+
+ @Test
+ public void testSetUpAndTearDown() {
+ }
+
+ @Test
+ public void testReboot() {
+ mCanonical.reboot();
+ verify(mRebootRunnable).run();
+ }
+
+ @Test
+ public void testGetProperties() throws Exception {
+ Properties properties = mCanonical.getProperties();
+
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
+ android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
+ (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
+ // It is OK for the SUT to cache the properties, so the underlying method doesn't
+ // need to be called every single time.
+ verify(driver, atMost(1)).getProperties_2_3(any());
+ TestUtil.validateDefaultProperties(properties, mSupportConcurrentCapture);
+ } else {
+ // It is OK for the SUT to cache the properties, so the underlying method doesn't
+ // need to be called every single time.
+ verify(mHalDriver, atMost(1)).getProperties(any());
+ TestUtil.validateDefaultProperties(properties, mSupportConcurrentCapture, 0, "");
+ }
+ }
+
+ private int loadGenericModel_2_0(ISoundTriggerHal.ModelCallback canonicalCallback)
+ throws Exception {
+ final int handle = 29;
+ ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel> modelCaptor =
+ ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel.class);
+ ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback> callbackCaptor =
+ ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.class);
+
+ doAnswer(invocation -> {
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHw.loadSoundModelCallback
+ resultCallback = invocation.getArgument(3);
+
+ // This is the return of this method.
+ resultCallback.onValues(0, handle);
+ return null;
+ }).when(mHalDriver).loadSoundModel(any(), any(), anyInt(), any());
+
+ assertEquals(handle,
+ mCanonical.loadSoundModel(TestUtil.createGenericSoundModel(), canonicalCallback));
+
+ verify(mHalDriver).loadSoundModel(modelCaptor.capture(), callbackCaptor.capture(), anyInt(),
+ any());
+
+ TestUtil.validateGenericSoundModel_2_0(modelCaptor.getValue());
+ validateCallback_2_0(callbackCaptor.getValue(), canonicalCallback);
+ return handle;
+ }
+
+ private int loadGenericModel_2_1(ISoundTriggerHal.ModelCallback canonicalCallback)
+ throws Exception {
+ final android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver_2_1 =
+ (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
+
+ final int handle = 29;
+ ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel> modelCaptor =
+ ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel.class);
+ ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
+ ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
+
+ doAnswer(invocation -> {
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadSoundModel_2_1Callback
+ resultCallback = invocation.getArgument(3);
+
+ // This is the return of this method.
+ resultCallback.onValues(0, handle);
+ return null;
+ }).when(driver_2_1).loadSoundModel_2_1(any(), any(), anyInt(), any());
+
+ assertEquals(handle,
+ mCanonical.loadSoundModel(TestUtil.createGenericSoundModel(), canonicalCallback));
+
+ verify(driver_2_1).loadSoundModel_2_1(modelCaptor.capture(), callbackCaptor.capture(),
+ anyInt(), any());
+
+ TestUtil.validateGenericSoundModel_2_1(modelCaptor.getValue());
+ validateCallback_2_1(callbackCaptor.getValue(), canonicalCallback);
+ return handle;
+ }
+
+ private int loadGenericModel_2_4(ISoundTriggerHal.ModelCallback canonicalCallback)
+ throws Exception {
+ final android.hardware.soundtrigger.V2_4.ISoundTriggerHw driver_2_4 =
+ (android.hardware.soundtrigger.V2_4.ISoundTriggerHw) mHalDriver;
+
+ final int handle = 29;
+ ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel> modelCaptor =
+ ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel.class);
+ ArgumentCaptor<android.hardware.soundtrigger.V2_4.ISoundTriggerHwCallback> callbackCaptor =
+ ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHwCallback.class);
+
+ doAnswer(invocation -> {
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHw.loadSoundModel_2_4Callback
+ resultCallback = invocation.getArgument(2);
+
+ // This is the return of this method.
+ resultCallback.onValues(0, handle);
+ return null;
+ }).when(driver_2_4).loadSoundModel_2_4(any(), any(), any());
+
+ assertEquals(handle,
+ mCanonical.loadSoundModel(TestUtil.createGenericSoundModel(), canonicalCallback));
+
+ verify(driver_2_4).loadSoundModel_2_4(modelCaptor.capture(), callbackCaptor.capture(),
+ any());
+
+ TestUtil.validateGenericSoundModel_2_1(modelCaptor.getValue());
+ validateCallback_2_4(callbackCaptor.getValue(), canonicalCallback);
+ return handle;
+ }
+
+ private int loadGenericModel(ISoundTriggerHal.ModelCallback canonicalCallback)
+ throws Exception {
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
+ return loadGenericModel_2_4(canonicalCallback);
+ } else if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
+ return loadGenericModel_2_1(canonicalCallback);
+ } else {
+ return loadGenericModel_2_0(canonicalCallback);
+ }
+ }
+
+ @Test
+ public void testLoadGenericModel() throws Exception {
+ ISoundTriggerHal.ModelCallback canonicalCallback = mock(
+ ISoundTriggerHal.ModelCallback.class);
+ loadGenericModel(canonicalCallback);
+ }
+
+ @Test
+ public void testMaxModels() throws Exception {
+ assumeFalse(mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw);
+
+ // Register global callback.
+ ISoundTriggerHal.GlobalCallback globalCallback = mock(
+ ISoundTriggerHal.GlobalCallback.class);
+ mCanonical.registerCallback(globalCallback);
+
+ ISoundTriggerHal.ModelCallback canonicalCallback = mock(
+ ISoundTriggerHal.ModelCallback.class);
+ final int maxModels = TestUtil.createDefaultProperties_2_0(false).maxSoundModels;
+ int[] modelHandles = new int[maxModels];
+
+ // Load as many models as we're allowed.
+ for (int i = 0; i < maxModels; ++i) {
+ modelHandles[i] = loadGenericModel(canonicalCallback);
+ verifyNoMoreInteractions(mHalDriver);
+ clearInvocations(mHalDriver);
+ }
+
+ // Now try to load an additional one and expect failure without invoking the underlying
+ // driver.
+ try {
+ mCanonical.loadPhraseSoundModel(TestUtil.createPhraseSoundModel(), canonicalCallback);
+ fail("Expected an exception");
+ } catch (RecoverableException e) {
+ assertEquals(Status.RESOURCE_CONTENTION, e.errorCode);
+ }
+
+ // Unload a single model and expect a onResourcesAvailable().
+ mCanonical.unloadSoundModel(modelHandles[0]);
+ verify(mHalDriver).unloadSoundModel(modelHandles[0]);
+
+ mCanonical.flushCallbacks();
+ verify(globalCallback).onResourcesAvailable();
+ }
+
+ private void testLoadGenericModelBusy_2_4() throws Exception {
+ final android.hardware.soundtrigger.V2_4.ISoundTriggerHw driver_2_4 =
+ (android.hardware.soundtrigger.V2_4.ISoundTriggerHw) mHalDriver;
+
+ doAnswer(invocation -> {
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHw.loadSoundModel_2_4Callback
+ resultCallback = invocation.getArgument(2);
+
+ // This is the return of this method.
+ resultCallback.onValues(-OsConstants.EBUSY, 0);
+ return null;
+ }).when(driver_2_4).loadSoundModel_2_4(any(), any(), any());
+
+ ISoundTriggerHal.ModelCallback canonicalCallback = mock(
+ ISoundTriggerHal.ModelCallback.class);
+ try {
+ mCanonical.loadSoundModel(TestUtil.createGenericSoundModel(), canonicalCallback);
+ fail("Expected an exception");
+ } catch (RecoverableException e) {
+ assertEquals(Status.RESOURCE_CONTENTION, e.errorCode);
+ }
+ verify(driver_2_4).loadSoundModel_2_4(any(), any(), any());
+ }
+
+ @Test
+ public void testLoadGenericModelBusy() throws Exception {
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
+ testLoadGenericModelBusy_2_4();
+ }
+ }
+
+ private int loadPhraseModel_2_0(ISoundTriggerHal.ModelCallback canonicalCallback)
+ throws Exception {
+ final int handle = 29;
+ ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel>
+ modelCaptor = ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel.class);
+ ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback> callbackCaptor =
+ ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.class);
+
+ doAnswer(invocation -> {
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHw.loadPhraseSoundModelCallback
+ resultCallback = invocation.getArgument(3);
+
+ // This is the return of this method.
+ resultCallback.onValues(0, handle);
+ return null;
+ }).when(mHalDriver).loadPhraseSoundModel(any(), any(), anyInt(), any());
+
+ assertEquals(handle, mCanonical.loadPhraseSoundModel(TestUtil.createPhraseSoundModel(),
+ canonicalCallback));
+
+ verify(mHalDriver).loadPhraseSoundModel(modelCaptor.capture(), callbackCaptor.capture(),
+ anyInt(), any());
+
+ TestUtil.validatePhraseSoundModel_2_0(modelCaptor.getValue());
+ validateCallback_2_0(callbackCaptor.getValue(), canonicalCallback);
+ return handle;
+ }
+
+ private int loadPhraseModel_2_1(ISoundTriggerHal.ModelCallback canonicalCallback)
+ throws Exception {
+ final android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver_2_1 =
+ (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
+
+ final int handle = 29;
+ ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel>
+ modelCaptor = ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel.class);
+ ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
+ ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
+
+ doAnswer(invocation -> {
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadPhraseSoundModel_2_1Callback
+ resultCallback = invocation.getArgument(3);
+
+ // This is the return of this method.
+ resultCallback.onValues(0, handle);
+ return null;
+ }).when(driver_2_1).loadPhraseSoundModel_2_1(any(), any(), anyInt(), any());
+
+ assertEquals(handle, mCanonical.loadPhraseSoundModel(TestUtil.createPhraseSoundModel(),
+ canonicalCallback));
+
+ verify(driver_2_1).loadPhraseSoundModel_2_1(modelCaptor.capture(), callbackCaptor.capture(),
+ anyInt(), any());
+
+ TestUtil.validatePhraseSoundModel_2_1(modelCaptor.getValue());
+ validateCallback_2_1(callbackCaptor.getValue(), canonicalCallback);
+ return handle;
+ }
+
+ private int loadPhraseModel_2_4(ISoundTriggerHal.ModelCallback canonicalCallback)
+ throws Exception {
+ final android.hardware.soundtrigger.V2_4.ISoundTriggerHw driver_2_4 =
+ (android.hardware.soundtrigger.V2_4.ISoundTriggerHw) mHalDriver;
+
+ final int handle = 29;
+ ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel>
+ modelCaptor = ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel.class);
+ ArgumentCaptor<android.hardware.soundtrigger.V2_4.ISoundTriggerHwCallback> callbackCaptor =
+ ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHwCallback.class);
+
+ doAnswer(invocation -> {
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHw.loadPhraseSoundModel_2_4Callback
+ resultCallback = invocation.getArgument(2);
+
+ // This is the return of this method.
+ resultCallback.onValues(0, handle);
+ return null;
+ }).when(driver_2_4).loadPhraseSoundModel_2_4(any(), any(), any());
+
+ assertEquals(handle, mCanonical.loadPhraseSoundModel(TestUtil.createPhraseSoundModel(),
+ canonicalCallback));
+
+ verify(driver_2_4).loadPhraseSoundModel_2_4(modelCaptor.capture(), callbackCaptor.capture(),
+ any());
+
+ TestUtil.validatePhraseSoundModel_2_1(modelCaptor.getValue());
+ validateCallback_2_4(callbackCaptor.getValue(), canonicalCallback);
+ return handle;
+ }
+
+ public int loadPhraseModel(ISoundTriggerHal.ModelCallback canonicalCallback) throws Exception {
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
+ return loadPhraseModel_2_4(canonicalCallback);
+ } else if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
+ return loadPhraseModel_2_1(canonicalCallback);
+ } else {
+ return loadPhraseModel_2_0(canonicalCallback);
+ }
+ }
+
+ @Test
+ public void testLoadPhraseModel() throws Exception {
+ ISoundTriggerHal.ModelCallback canonicalCallback = mock(
+ ISoundTriggerHal.ModelCallback.class);
+ loadPhraseModel(canonicalCallback);
+ }
+
+ private void testLoadPhraseModelBusy_2_4() throws Exception {
+ final android.hardware.soundtrigger.V2_4.ISoundTriggerHw driver_2_4 =
+ (android.hardware.soundtrigger.V2_4.ISoundTriggerHw) mHalDriver;
+
+ doAnswer(invocation -> {
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHw.loadPhraseSoundModel_2_4Callback
+ resultCallback = invocation.getArgument(2);
+
+ // This is the return of this method.
+ resultCallback.onValues(-OsConstants.EBUSY, 0);
+ return null;
+ }).when(driver_2_4).loadPhraseSoundModel_2_4(any(), any(), any());
+
+ ISoundTriggerHal.ModelCallback canonicalCallback = mock(
+ ISoundTriggerHal.ModelCallback.class);
+ try {
+ mCanonical.loadPhraseSoundModel(TestUtil.createPhraseSoundModel(), canonicalCallback);
+ fail("Expected an exception");
+ } catch (RecoverableException e) {
+ assertEquals(Status.RESOURCE_CONTENTION, e.errorCode);
+ }
+ verify(driver_2_4).loadPhraseSoundModel_2_4(any(), any(), any());
+ }
+
+ @Test
+ public void testLoadPhraseModelBusy() throws Exception {
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
+ testLoadPhraseModelBusy_2_4();
+ }
+ }
+
+ @Test
+ public void testUnloadModel() throws Exception {
+ mCanonical.unloadSoundModel(14);
+ verify(mHalDriver).unloadSoundModel(14);
+ }
+
+ private void startRecognition_2_0(int handle, ISoundTriggerHal.ModelCallback canonicalCallback)
+ throws Exception {
+ ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig>
+ configCaptor = ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig.class);
+ ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback> callbackCaptor =
+ ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.class);
+
+ when(mHalDriver.startRecognition(eq(handle), any(), any(), anyInt())).thenReturn(0);
+
+ RecognitionConfig config = TestUtil.createRecognitionConfig();
+ mCanonical.startRecognition(handle, 203, 204, config);
+ verify(mHalDriver).startRecognition(eq(handle), configCaptor.capture(),
+ callbackCaptor.capture(), anyInt());
+
+ TestUtil.validateRecognitionConfig_2_0(configCaptor.getValue(), 203, 204);
+ validateCallback_2_0(callbackCaptor.getValue(), canonicalCallback);
+ }
+
+ private void startRecognition_2_1(int handle, ISoundTriggerHal.ModelCallback canonicalCallback)
+ throws Exception {
+ final android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver_2_1 =
+ (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
+
+ ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig>
+ configCaptor = ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig.class);
+ ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
+ ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
+
+ when(driver_2_1.startRecognition_2_1(eq(handle), any(), any(), anyInt())).thenReturn(0);
+
+ RecognitionConfig config = TestUtil.createRecognitionConfig();
+ mCanonical.startRecognition(handle, 505, 506, config);
+ verify(driver_2_1).startRecognition_2_1(eq(handle), configCaptor.capture(),
+ callbackCaptor.capture(), anyInt());
+
+ TestUtil.validateRecognitionConfig_2_1(configCaptor.getValue(), 505, 506);
+ validateCallback_2_1(callbackCaptor.getValue(), canonicalCallback);
+ }
+
+ private void startRecognition_2_3(int handle) throws Exception {
+ final android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver_2_3 =
+ (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
+ ArgumentCaptor<android.hardware.soundtrigger.V2_3.RecognitionConfig> configCaptor =
+ ArgumentCaptor.forClass(android.hardware.soundtrigger.V2_3.RecognitionConfig.class);
+
+ when(driver_2_3.startRecognition_2_3(eq(handle), any())).thenReturn(0);
+
+ RecognitionConfig config = TestUtil.createRecognitionConfig();
+ mCanonical.startRecognition(handle, 808, 909, config);
+ verify(driver_2_3).startRecognition_2_3(eq(handle), configCaptor.capture());
+ TestUtil.validateRecognitionConfig_2_3(configCaptor.getValue(), 808, 909);
+ }
+
+ private void startRecognition_2_4(int handle) throws Exception {
+ final android.hardware.soundtrigger.V2_4.ISoundTriggerHw driver_2_4 =
+ (android.hardware.soundtrigger.V2_4.ISoundTriggerHw) mHalDriver;
+ ArgumentCaptor<android.hardware.soundtrigger.V2_3.RecognitionConfig> configCaptor =
+ ArgumentCaptor.forClass(android.hardware.soundtrigger.V2_3.RecognitionConfig.class);
+
+ when(driver_2_4.startRecognition_2_4(eq(handle), any())).thenReturn(0);
+
+ RecognitionConfig config = TestUtil.createRecognitionConfig();
+ mCanonical.startRecognition(handle, 21, 22, config);
+ verify(driver_2_4).startRecognition_2_4(eq(handle), configCaptor.capture());
+ TestUtil.validateRecognitionConfig_2_3(configCaptor.getValue(), 21, 22);
+ }
+
+ private void startRecognition(int handle, ISoundTriggerHal.ModelCallback canonicalCallback)
+ throws Exception {
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
+ startRecognition_2_4(handle);
+ } else if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
+ startRecognition_2_3(handle);
+ } else if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
+ startRecognition_2_1(handle, canonicalCallback);
+ } else {
+ startRecognition_2_0(handle, canonicalCallback);
+ }
+ }
+
+ @Test
+ public void testStartRecognition() throws Exception {
+ // First load.
+ ISoundTriggerHal.ModelCallback canonicalCallback = mock(
+ ISoundTriggerHal.ModelCallback.class);
+ final int handle = loadGenericModel(canonicalCallback);
+
+ // Then start.
+ startRecognition(handle, canonicalCallback);
+ }
+
+ private void testStartRecognitionBusy_2_4() throws Exception {
+ final android.hardware.soundtrigger.V2_4.ISoundTriggerHw driver_2_4 =
+ (android.hardware.soundtrigger.V2_4.ISoundTriggerHw) mHalDriver;
+
+ final int handle = 68;
+ when(driver_2_4.startRecognition_2_4(eq(handle), any())).thenReturn(-OsConstants.EBUSY);
+
+ RecognitionConfig config = TestUtil.createRecognitionConfig();
+ try {
+ mCanonical.startRecognition(handle, 34, 35, config);
+ fail("Expected an exception");
+ } catch (RecoverableException e) {
+ assertEquals(Status.RESOURCE_CONTENTION, e.errorCode);
+ }
+ verify(driver_2_4).startRecognition_2_4(eq(handle), any());
+ }
+
+ @Test
+ public void testStartRecognitionBusy() throws Exception {
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
+ testStartRecognitionBusy_2_4();
+ }
+ }
+
+ @Test
+ public void testNoRegisterCaptureStateListener() {
+ assumeTrue(mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw
+ || mSupportConcurrentCapture);
+ verify(mCaptureStateNotifier, never()).registerListener(any());
+ }
+
+ @Test
+ public void testConcurrentCaptureAbort() throws Exception {
+ assumeFalse(mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw
+ || mSupportConcurrentCapture);
+ verify(mCaptureStateNotifier, atLeast(1)).registerListener(any());
+
+ // Register global callback.
+ ISoundTriggerHal.GlobalCallback globalCallback = mock(
+ ISoundTriggerHal.GlobalCallback.class);
+ mCanonical.registerCallback(globalCallback);
+
+ // Load.
+ ISoundTriggerHal.ModelCallback canonicalCallback = mock(
+ ISoundTriggerHal.ModelCallback.class);
+ final int handle = loadGenericModel(canonicalCallback);
+
+ // Then start.
+ startRecognition(handle, canonicalCallback);
+
+ // Now activate external capture.
+ mCaptureStateNotifier.setState(true);
+
+ // Expect hardware to have been stopped.
+ verify(mHalDriver).stopRecognition(handle);
+
+ // Expect an abort event (async).
+ ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+ RecognitionEvent.class);
+ mCanonical.flushCallbacks();
+ verify(canonicalCallback).recognitionCallback(eq(handle), eventCaptor.capture());
+ assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().status);
+
+ // Deactivate external capture.
+ mCaptureStateNotifier.setState(false);
+
+ // Expect a onResourcesAvailable().
+ mCanonical.flushCallbacks();
+ verify(globalCallback).onResourcesAvailable();
+ }
+
+ @Test
+ public void testConcurrentCaptureReject() throws Exception {
+ assumeFalse(mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw
+ || mSupportConcurrentCapture);
+ verify(mCaptureStateNotifier, atLeast(1)).registerListener(any());
+
+ // Register global callback.
+ ISoundTriggerHal.GlobalCallback globalCallback = mock(
+ ISoundTriggerHal.GlobalCallback.class);
+ mCanonical.registerCallback(globalCallback);
+
+ // Load (this registers the callback).
+ ISoundTriggerHal.ModelCallback canonicalCallback = mock(
+ ISoundTriggerHal.ModelCallback.class);
+ final int handle = loadGenericModel(canonicalCallback);
+
+ // Report external capture active.
+ mCaptureStateNotifier.setState(true);
+
+ // Then start.
+ RecognitionConfig config = TestUtil.createRecognitionConfig();
+ try {
+ mCanonical.startRecognition(handle, 203, 204, config);
+ fail("Expected an exception");
+ } catch (RecoverableException e) {
+ assertEquals(Status.RESOURCE_CONTENTION, e.errorCode);
+ }
+
+ // Deactivate external capture.
+ mCaptureStateNotifier.setState(false);
+
+ // Expect a onResourcesAvailable().
+ mCanonical.flushCallbacks();
+ verify(globalCallback).onResourcesAvailable();
+ }
+
+ @Test
+ public void testStopRecognition() throws Exception {
+ mCanonical.stopRecognition(17);
+ verify(mHalDriver).stopRecognition(17);
+ }
+
+ @Test
+ public void testForceRecognition() throws Exception {
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_2.ISoundTriggerHw) {
+ android.hardware.soundtrigger.V2_2.ISoundTriggerHw driver_2_2 =
+ (android.hardware.soundtrigger.V2_2.ISoundTriggerHw) mHalDriver;
+ mCanonical.forceRecognitionEvent(14);
+ verify(driver_2_2).getModelState(14);
+ } else {
+ try {
+ mCanonical.forceRecognitionEvent(14);
+ fail("Expected an exception");
+ } catch (RecoverableException e) {
+ assertEquals(Status.OPERATION_NOT_SUPPORTED, e.errorCode);
+ }
+ }
+ }
+
+ @Test
+ public void testGetParameter() throws Exception {
+ assumeTrue(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw);
+
+ android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver_2_3 =
+ (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
+
+ doAnswer(invocation -> {
+ android.hardware.soundtrigger.V2_3.ISoundTriggerHw.getParameterCallback resultCallback =
+ invocation.getArgument(2);
+
+ // This is the return of this method.
+ resultCallback.onValues(0, 99);
+ return null;
+ }).when(driver_2_3).getParameter(eq(21), eq(47), any());
+
+ assertEquals(99, mCanonical.getModelParameter(21, 47));
+ verify(driver_2_3).getParameter(eq(21), eq(47), any());
+ }
+
+ @Test
+ public void testSetParameter() throws Exception {
+ assumeTrue(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw);
+
+ android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver_2_3 =
+ (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
+
+ mCanonical.setModelParameter(212, 247, 80);
+ verify(driver_2_3).setParameter(212, 247, 80);
+ }
+
+ @Test
+ public void testQueryParameterSupported() throws Exception {
+ assumeTrue(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw);
+
+ android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver_2_3 =
+ (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
+
+ doAnswer(invocation -> {
+ android.hardware.soundtrigger.V2_3.ISoundTriggerHw.queryParameterCallback
+ resultCallback = invocation.getArgument(2);
+
+ // This is the return of this method.
+ android.hardware.soundtrigger.V2_3.ModelParameterRange range =
+ new android.hardware.soundtrigger.V2_3.ModelParameterRange();
+ range.start = 34;
+ range.end = 45;
+ android.hardware.soundtrigger.V2_3.OptionalModelParameterRange optionalRange =
+ new android.hardware.soundtrigger.V2_3.OptionalModelParameterRange();
+ optionalRange.range(range);
+ resultCallback.onValues(0, optionalRange);
+ return null;
+ }).when(driver_2_3).queryParameter(eq(11), eq(12), any());
+
+ ModelParameterRange range = mCanonical.queryParameter(11, 12);
+ assertNotNull(range);
+ assertEquals(34, range.minInclusive);
+ assertEquals(45, range.maxInclusive);
+ verify(driver_2_3).queryParameter(eq(11), eq(12), any());
+ }
+
+ @Test
+ public void testQueryParameterNotSupported() throws Exception {
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
+ android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver_2_3 =
+ (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
+
+ doAnswer(invocation -> {
+ android.hardware.soundtrigger.V2_3.ISoundTriggerHw.queryParameterCallback
+ resultCallback = invocation.getArgument(2);
+
+ // This is the return of this method.
+ android.hardware.soundtrigger.V2_3.OptionalModelParameterRange optionalRange =
+ new android.hardware.soundtrigger.V2_3.OptionalModelParameterRange();
+ resultCallback.onValues(0, optionalRange);
+ return null;
+ }).when(driver_2_3).queryParameter(eq(11), eq(12), any());
+
+ ModelParameterRange range = mCanonical.queryParameter(11, 12);
+ assertNull(range);
+ verify(driver_2_3).queryParameter(eq(11), eq(12), any());
+ } else {
+ ModelParameterRange range = mCanonical.queryParameter(11, 12);
+ assertNull(range);
+ }
+ }
+
+ private void testGlobalCallback_2_0() {
+ ISoundTriggerHal.GlobalCallback canonicalCallback = mock(
+ ISoundTriggerHal.GlobalCallback.class);
+ mCanonical.registerCallback(canonicalCallback);
+ // We just care that it doesn't throw.
+ }
+
+ private void testGlobalCallback_2_4() throws Exception {
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHw driver_2_4 =
+ (android.hardware.soundtrigger.V2_4.ISoundTriggerHw) mHalDriver;
+
+ ISoundTriggerHal.GlobalCallback canonicalCallback = mock(
+ ISoundTriggerHal.GlobalCallback.class);
+ mCanonical.registerCallback(canonicalCallback);
+
+ ArgumentCaptor<android.hardware.soundtrigger.V2_4.ISoundTriggerHwGlobalCallback>
+ callbackCaptor = ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHwGlobalCallback.class);
+ verify(driver_2_4).registerGlobalCallback(callbackCaptor.capture());
+ validateGlobalCallback_2_4(callbackCaptor.getValue(), canonicalCallback);
+ }
+
+ @Test
+ public void testGlobalCallback() throws Exception {
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
+ testGlobalCallback_2_4();
+ } else {
+ testGlobalCallback_2_0();
+ }
+ }
+
+ @Test
+ public void testLinkToDeath() throws Exception {
+ IBinder.DeathRecipient canonicalRecipient = mock(IBinder.DeathRecipient.class);
+ when(mHalDriver.linkToDeath(any(), anyLong())).thenReturn(true);
+ mCanonical.linkToDeath(canonicalRecipient);
+
+ ArgumentCaptor<IHwBinder.DeathRecipient> recipientCaptor = ArgumentCaptor.forClass(
+ IHwBinder.DeathRecipient.class);
+ ArgumentCaptor<Long> cookieCaptor = ArgumentCaptor.forClass(Long.class);
+ verify(mHalDriver).linkToDeath(recipientCaptor.capture(), cookieCaptor.capture());
+
+ recipientCaptor.getValue().serviceDied(cookieCaptor.getValue());
+ mCanonical.flushCallbacks();
+ verify(canonicalRecipient).binderDied();
+
+ mCanonical.unlinkToDeath(canonicalRecipient);
+ verify(mHalDriver).unlinkToDeath(recipientCaptor.getValue());
+ }
+
+ @Test
+ public void testInterfaceDescriptor() throws Exception {
+ when(mHalDriver.interfaceDescriptor()).thenReturn("ABCD");
+ assertEquals("ABCD", mCanonical.interfaceDescriptor());
+ verify(mHalDriver).interfaceDescriptor();
+ }
+
+ private void validateGlobalCallback_2_4(
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHwGlobalCallback hwCallback,
+ ISoundTriggerHal.GlobalCallback canonicalCallback) throws Exception {
+ hwCallback.onResourcesAvailable();
+ mCanonical.flushCallbacks();
+ verify(canonicalCallback).onResourcesAvailable();
+ }
+
+ private void validateCallback_2_0(
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback hwCallback,
+ ISoundTriggerHal.ModelCallback canonicalCallback) throws Exception {
+ {
+ final int handle = 85;
+ final int status =
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.ABORT;
+ ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+ RecognitionEvent.class);
+
+ hwCallback.recognitionCallback(TestUtil.createRecognitionEvent_2_0(handle, status), 99);
+ mCanonical.flushCallbacks();
+ verify(canonicalCallback).recognitionCallback(eq(handle), eventCaptor.capture());
+ TestUtil.validateRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.ABORTED);
+ }
+
+ {
+ final int handle = 92;
+ final int status =
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.SUCCESS;
+ ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+ PhraseRecognitionEvent.class);
+
+ hwCallback.phraseRecognitionCallback(
+ TestUtil.createPhraseRecognitionEvent_2_0(handle, status), 99);
+ mCanonical.flushCallbacks();
+ verify(canonicalCallback).phraseRecognitionCallback(eq(handle), eventCaptor.capture());
+ TestUtil.validatePhraseRecognitionEvent(eventCaptor.getValue(),
+ RecognitionStatus.SUCCESS);
+ }
+ verifyNoMoreInteractions(canonicalCallback);
+ clearInvocations(canonicalCallback);
+ }
+
+ private void validateCallback_2_1(
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback hwCallback,
+ ISoundTriggerHal.ModelCallback canonicalCallback) throws Exception {
+ {
+ final int handle = 85;
+ final int status =
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.ABORT;
+ ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+ RecognitionEvent.class);
+
+ hwCallback.recognitionCallback_2_1(TestUtil.createRecognitionEvent_2_1(handle, status),
+ 99);
+ mCanonical.flushCallbacks();
+ verify(canonicalCallback).recognitionCallback(eq(handle), eventCaptor.capture());
+ TestUtil.validateRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.ABORTED);
+ }
+
+ {
+ final int handle = 92;
+ final int status =
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.SUCCESS;
+ ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+ PhraseRecognitionEvent.class);
+
+ hwCallback.phraseRecognitionCallback_2_1(
+ TestUtil.createPhraseRecognitionEvent_2_1(handle, status), 99);
+ mCanonical.flushCallbacks();
+ verify(canonicalCallback).phraseRecognitionCallback(eq(handle), eventCaptor.capture());
+ TestUtil.validatePhraseRecognitionEvent(eventCaptor.getValue(),
+ RecognitionStatus.SUCCESS);
+ }
+ verifyNoMoreInteractions(canonicalCallback);
+ clearInvocations(canonicalCallback);
+ }
+
+ private void validateCallback_2_4(
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHwCallback hwCallback,
+ ISoundTriggerHal.ModelCallback canonicalCallback) throws Exception {
+ {
+ final int handle = 85;
+ final int status =
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.ABORT;
+ ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+ RecognitionEvent.class);
+
+ hwCallback.recognitionCallback_2_1(TestUtil.createRecognitionEvent_2_1(handle, status),
+ 99);
+ mCanonical.flushCallbacks();
+ verify(canonicalCallback).recognitionCallback(eq(handle), eventCaptor.capture());
+ TestUtil.validateRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.ABORTED);
+ }
+
+ {
+ final int handle = 92;
+ final int status =
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.SUCCESS;
+ ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+ PhraseRecognitionEvent.class);
+
+ hwCallback.phraseRecognitionCallback_2_1(
+ TestUtil.createPhraseRecognitionEvent_2_1(handle, status), 99);
+ mCanonical.flushCallbacks();
+ verify(canonicalCallback).phraseRecognitionCallback(eq(handle), eventCaptor.capture());
+ TestUtil.validatePhraseRecognitionEvent(eventCaptor.getValue(),
+ RecognitionStatus.SUCCESS);
+ }
+
+ {
+ final int handle = 23;
+ hwCallback.modelUnloaded(handle);
+ mCanonical.flushCallbacks();
+ verify(canonicalCallback).modelUnloaded(handle);
+ }
+ verifyNoMoreInteractions(canonicalCallback);
+ clearInvocations(canonicalCallback);
+ }
+
+ public static class CaptureStateNotifier implements ICaptureStateNotifier {
+ private final List<Listener> mListeners = new LinkedList<>();
+
+ @Override
+ public boolean registerListener(Listener listener) {
+ mListeners.add(listener);
+ return false;
+ }
+
+ @Override
+ public void unregisterListener(Listener listener) {
+ mListeners.remove(listener);
+ }
+
+ public void setState(boolean state) {
+ for (Listener listener : mListeners) {
+ listener.onCaptureStateChange(state);
+ }
+ }
+
+ public void verifyNoMoreListeners() {
+ assertEquals(0, mListeners.size());
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
index 509eb25..1daf831 100644
--- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
@@ -16,89 +16,50 @@
package com.android.server.soundtrigger_middleware;
-import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.hardware.audio.common.V2_0.AudioConfig;
-import android.hardware.audio.common.V2_0.Uuid;
-import android.hardware.soundtrigger.V2_3.OptionalModelParameterRange;
-import android.media.audio.common.AudioChannelMask;
-import android.media.audio.common.AudioFormat;
-import android.media.soundtrigger_middleware.AudioCapabilities;
-import android.media.soundtrigger_middleware.ConfidenceLevel;
+import android.media.soundtrigger.ModelParameter;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Status;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
-import android.media.soundtrigger_middleware.ModelParameter;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.Phrase;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.RecognitionMode;
-import android.media.soundtrigger_middleware.RecognitionStatus;
-import android.media.soundtrigger_middleware.SoundModel;
-import android.media.soundtrigger_middleware.SoundModelType;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
-import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
-import android.os.HidlMemoryUtil;
-import android.os.HwParcel;
-import android.os.IHwBinder;
-import android.os.IHwInterface;
-import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
-import android.os.SharedMemory;
-import android.system.ErrnoException;
import android.util.Pair;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
+import org.junit.runners.JUnit4;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
-import org.mockito.stubbing.Answer;
-import java.io.FileDescriptor;
-import java.nio.ByteBuffer;
-
-@RunWith(Parameterized.class)
+@RunWith(JUnit4.class)
public class SoundTriggerMiddlewareImplTest {
- private static final String TAG = "SoundTriggerMiddlewareImplTest";
+ @Mock public ISoundTriggerHal mHalDriver = mock(ISoundTriggerHal.class);
- // We run the test once for every version of the underlying driver.
- @Parameterized.Parameters
- public static Object[] data() {
- return new Object[]{
- mock(android.hardware.soundtrigger.V2_0.ISoundTriggerHw.class),
- mock(android.hardware.soundtrigger.V2_1.ISoundTriggerHw.class),
- mock(android.hardware.soundtrigger.V2_2.ISoundTriggerHw.class),
- mock(android.hardware.soundtrigger.V2_3.ISoundTriggerHw.class),
- };
- }
-
- @Mock
- @Parameterized.Parameter
- public android.hardware.soundtrigger.V2_0.ISoundTriggerHw mHalDriver;
-
- @Mock
- private SoundTriggerMiddlewareImpl.AudioSessionProvider mAudioSessionProvider = mock(
- SoundTriggerMiddlewareImpl.AudioSessionProvider.class);
+ @Mock private final SoundTriggerMiddlewareImpl.AudioSessionProvider mAudioSessionProvider =
+ mock(SoundTriggerMiddlewareImpl.AudioSessionProvider.class);
private SoundTriggerMiddlewareImpl mService;
@@ -106,522 +67,41 @@
return mock(ISoundTriggerCallback.Stub.class, Mockito.CALLS_REAL_METHODS);
}
- private static SoundModel createGenericSoundModel() {
- return createSoundModel(SoundModelType.GENERIC);
- }
-
- private static FileDescriptor byteArrayToFileDescriptor(byte[] data) {
- try {
- SharedMemory shmem = SharedMemory.create("", data.length);
- ByteBuffer buffer = shmem.mapReadWrite();
- buffer.put(data);
- return shmem.getFileDescriptor();
- } catch (ErrnoException e) {
- throw new RuntimeException(e);
- }
- }
-
- private static SoundModel createSoundModel(int type) {
- SoundModel model = new SoundModel();
- model.type = type;
- model.uuid = "12345678-2345-3456-4567-abcdef987654";
- model.vendorUuid = "87654321-5432-6543-7654-456789fedcba";
- byte[] data = new byte[]{91, 92, 93, 94, 95};
- model.data = new ParcelFileDescriptor(byteArrayToFileDescriptor(data));
- model.dataSize = data.length;
- return model;
- }
-
- private static PhraseSoundModel createPhraseSoundModel() {
- PhraseSoundModel model = new PhraseSoundModel();
- model.common = createSoundModel(SoundModelType.KEYPHRASE);
- model.phrases = new Phrase[1];
- model.phrases[0] = new Phrase();
- model.phrases[0].id = 123;
- model.phrases[0].users = new int[]{5, 6, 7};
- model.phrases[0].locale = "locale";
- model.phrases[0].text = "text";
- model.phrases[0].recognitionModes =
- RecognitionMode.USER_AUTHENTICATION | RecognitionMode.USER_IDENTIFICATION;
- return model;
- }
-
- private static android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties createDefaultProperties(
- boolean supportConcurrentCapture) {
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties properties =
- new android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties();
- properties.implementor = "implementor";
- properties.description = "description";
- properties.version = 123;
- properties.uuid = new Uuid();
- properties.uuid.timeLow = 1;
- properties.uuid.timeMid = 2;
- properties.uuid.versionAndTimeHigh = 3;
- properties.uuid.variantAndClockSeqHigh = 4;
- properties.uuid.node = new byte[]{5, 6, 7, 8, 9, 10};
-
- properties.maxSoundModels = 456;
- properties.maxKeyPhrases = 567;
- properties.maxUsers = 678;
- properties.recognitionModes =
- android.hardware.soundtrigger.V2_0.RecognitionMode.VOICE_TRIGGER
- | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_IDENTIFICATION
- | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_AUTHENTICATION
- | android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER;
- properties.captureTransition = true;
- properties.maxBufferMs = 321;
- properties.concurrentCapture = supportConcurrentCapture;
- properties.triggerInEvent = true;
- properties.powerConsumptionMw = 432;
- return properties;
- }
-
- private static android.hardware.soundtrigger.V2_3.Properties createDefaultProperties_2_3(
- boolean supportConcurrentCapture) {
- android.hardware.soundtrigger.V2_3.Properties properties =
- new android.hardware.soundtrigger.V2_3.Properties();
- properties.base = createDefaultProperties(supportConcurrentCapture);
- properties.supportedModelArch = "supportedModelArch";
- properties.audioCapabilities =
- android.hardware.soundtrigger.V2_3.AudioCapabilities.ECHO_CANCELLATION
- | android.hardware.soundtrigger.V2_3.AudioCapabilities.NOISE_SUPPRESSION;
- return properties;
- }
-
- private void validateDefaultProperties(SoundTriggerModuleProperties properties,
- boolean supportConcurrentCapture) {
- assertEquals("implementor", properties.implementor);
- assertEquals("description", properties.description);
- assertEquals(123, properties.version);
- assertEquals("00000001-0002-0003-0004-05060708090a", properties.uuid);
- assertEquals(456, properties.maxSoundModels);
- assertEquals(567, properties.maxKeyPhrases);
- assertEquals(678, properties.maxUsers);
- assertEquals(RecognitionMode.GENERIC_TRIGGER
- | RecognitionMode.USER_AUTHENTICATION
- | RecognitionMode.USER_IDENTIFICATION
- | RecognitionMode.VOICE_TRIGGER, properties.recognitionModes);
- assertTrue(properties.captureTransition);
- assertEquals(321, properties.maxBufferMs);
- assertEquals(supportConcurrentCapture, properties.concurrentCapture);
- assertTrue(properties.triggerInEvent);
- assertEquals(432, properties.powerConsumptionMw);
-
- if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
- assertEquals("supportedModelArch", properties.supportedModelArch);
- assertEquals(AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION,
- properties.audioCapabilities);
- } else {
- assertEquals("", properties.supportedModelArch);
- assertEquals(0, properties.audioCapabilities);
- }
- }
-
- private void verifyNotGetProperties() throws RemoteException {
- if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
- verify((android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver,
- never()).getProperties(any());
- }
- }
-
- private static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_0(
- int hwHandle,
- int status) {
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent halEvent =
- new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent();
- halEvent.status = status;
- halEvent.type = SoundModelType.GENERIC;
- halEvent.model = hwHandle;
- halEvent.captureAvailable = true;
- // This field is ignored.
- halEvent.captureSession = 123;
- halEvent.captureDelayMs = 234;
- halEvent.capturePreambleMs = 345;
- halEvent.triggerInData = true;
- halEvent.audioConfig = new AudioConfig();
- halEvent.audioConfig.sampleRateHz = 456;
- halEvent.audioConfig.channelMask = AudioChannelMask.IN_LEFT;
- halEvent.audioConfig.format = AudioFormat.MP3;
- // hwEvent.audioConfig.offloadInfo is irrelevant.
- halEvent.data.add((byte) 31);
- halEvent.data.add((byte) 32);
- halEvent.data.add((byte) 33);
- return halEvent;
- }
-
- private static android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_1(
- int hwHandle,
- int status) {
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent halEvent =
- new android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent();
- halEvent.header = createRecognitionEvent_2_0(hwHandle, status);
- halEvent.header.data.clear();
- halEvent.data = HidlMemoryUtil.byteArrayToHidlMemory(new byte[]{31, 32, 33});
- return halEvent;
- }
-
- private static void validateRecognitionEvent(RecognitionEvent event, int status) {
- assertEquals(status, event.status);
- assertEquals(SoundModelType.GENERIC, event.type);
- assertTrue(event.captureAvailable);
- assertEquals(101, event.captureSession);
- assertEquals(234, event.captureDelayMs);
- assertEquals(345, event.capturePreambleMs);
- assertTrue(event.triggerInData);
- assertEquals(456, event.audioConfig.sampleRateHz);
- assertEquals(AudioChannelMask.IN_LEFT, event.audioConfig.channelMask);
- assertEquals(AudioFormat.MP3, event.audioConfig.format);
- }
-
- private static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent createPhraseRecognitionEvent_2_0(
- int hwHandle, int status) {
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent halEvent =
- new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent();
- halEvent.common = createRecognitionEvent_2_0(hwHandle, status);
-
- android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halExtra =
- new android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra();
- halExtra.id = 123;
- halExtra.confidenceLevel = 52;
- halExtra.recognitionModes = android.hardware.soundtrigger.V2_0.RecognitionMode.VOICE_TRIGGER
- | android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER;
- android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel =
- new android.hardware.soundtrigger.V2_0.ConfidenceLevel();
- halLevel.userId = 31;
- halLevel.levelPercent = 43;
- halExtra.levels.add(halLevel);
- halEvent.phraseExtras.add(halExtra);
- return halEvent;
- }
-
- private static android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent createPhraseRecognitionEvent_2_1(
- int hwHandle, int status) {
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent halEvent =
- new android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent();
- halEvent.common = createRecognitionEvent_2_1(hwHandle, status);
-
- android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halExtra =
- new android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra();
- halExtra.id = 123;
- halExtra.confidenceLevel = 52;
- halExtra.recognitionModes = android.hardware.soundtrigger.V2_0.RecognitionMode.VOICE_TRIGGER
- | android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER;
- android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel =
- new android.hardware.soundtrigger.V2_0.ConfidenceLevel();
- halLevel.userId = 31;
- halLevel.levelPercent = 43;
- halExtra.levels.add(halLevel);
- halEvent.phraseExtras.add(halExtra);
- return halEvent;
- }
-
- private static void validatePhraseRecognitionEvent(PhraseRecognitionEvent event, int status) {
- validateRecognitionEvent(event.common, status);
-
- assertEquals(1, event.phraseExtras.length);
- assertEquals(123, event.phraseExtras[0].id);
- assertEquals(52, event.phraseExtras[0].confidenceLevel);
- assertEquals(RecognitionMode.VOICE_TRIGGER | RecognitionMode.GENERIC_TRIGGER,
- event.phraseExtras[0].recognitionModes);
- assertEquals(1, event.phraseExtras[0].levels.length);
- assertEquals(31, event.phraseExtras[0].levels[0].userId);
- assertEquals(43, event.phraseExtras[0].levels[0].levelPercent);
- }
-
- private void initService(boolean supportConcurrentCapture) throws RemoteException {
- doAnswer(invocation -> {
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties properties =
- createDefaultProperties(
- supportConcurrentCapture);
- ((android.hardware.soundtrigger.V2_0.ISoundTriggerHw.getPropertiesCallback) invocation.getArgument(
- 0)).onValues(0,
- properties);
- return null;
- }).when(mHalDriver).getProperties(any());
-
- if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
- android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
- (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
- doAnswer(invocation -> {
- android.hardware.soundtrigger.V2_3.Properties properties =
- createDefaultProperties_2_3(
- supportConcurrentCapture);
- ((android.hardware.soundtrigger.V2_3.ISoundTriggerHw.getProperties_2_3Callback)
- invocation.getArgument(
- 0)).onValues(0,
- properties);
- return null;
- }).when(driver).getProperties_2_3(any());
- }
-
- mService = new SoundTriggerMiddlewareImpl(() -> {
- return mHalDriver;
- }, mAudioSessionProvider);
- }
-
- private Pair<Integer, SoundTriggerHwCallback> loadGenericModel_2_0(ISoundTriggerModule module,
- int hwHandle) throws RemoteException {
- SoundModel model = createGenericSoundModel();
- ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel> modelCaptor =
- ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel.class);
- ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback> callbackCaptor =
- ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.class);
- ArgumentCaptor<Integer> cookieCaptor = ArgumentCaptor.forClass(Integer.class);
-
- doAnswer(invocation -> {
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback callback =
- invocation.getArgument(1);
- int callbackCookie = invocation.getArgument(2);
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw.loadSoundModelCallback
- resultCallback = invocation.getArgument(3);
-
- // This is the return of this method.
- resultCallback.onValues(0, hwHandle);
-
- // This is the async mCallback that comes after.
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.ModelEvent modelEvent =
- new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.ModelEvent();
- modelEvent.status =
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.SoundModelStatus.UPDATED;
- modelEvent.model = hwHandle;
- callback.soundModelCallback(modelEvent, callbackCookie);
- return null;
- }).when(mHalDriver).loadSoundModel(modelCaptor.capture(), callbackCaptor.capture(),
- cookieCaptor.capture(), any());
-
- when(mAudioSessionProvider.acquireSession()).thenReturn(
- new SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession(101, 102, 103));
-
- int handle = module.loadModel(model);
- verify(mHalDriver).loadSoundModel(any(), any(), anyInt(), any());
- verify(mAudioSessionProvider).acquireSession();
-
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel hidlModel =
- modelCaptor.getValue();
- assertEquals(android.hardware.soundtrigger.V2_0.SoundModelType.GENERIC,
- hidlModel.type);
- assertEquals(model.uuid, ConversionUtil.hidl2aidlUuid(hidlModel.uuid));
- assertEquals(model.vendorUuid, ConversionUtil.hidl2aidlUuid(hidlModel.vendorUuid));
- assertArrayEquals(new Byte[]{91, 92, 93, 94, 95}, hidlModel.data.toArray());
-
- return new Pair<>(handle,
- new SoundTriggerHwCallback(callbackCaptor.getValue(), cookieCaptor.getValue()));
- }
-
- private Pair<Integer, SoundTriggerHwCallback> loadGenericModel_2_1(ISoundTriggerModule module,
- int hwHandle) throws RemoteException {
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver =
- (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
- SoundModel model = createGenericSoundModel();
- ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel> modelCaptor =
- ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel.class);
- ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
- ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
- ArgumentCaptor<Integer> cookieCaptor = ArgumentCaptor.forClass(Integer.class);
-
- doAnswer(invocation -> {
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback callback =
- invocation.getArgument(1);
- int callbackCookie = invocation.getArgument(2);
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadSoundModel_2_1Callback
- resultCallback = invocation.getArgument(3);
-
- // This is the return of this method.
- resultCallback.onValues(0, hwHandle);
-
- // This is the async mCallback that comes after.
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.ModelEvent modelEvent =
- new android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.ModelEvent();
- modelEvent.header.status =
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.SoundModelStatus.UPDATED;
- modelEvent.header.model = hwHandle;
- callback.soundModelCallback_2_1(modelEvent, callbackCookie);
- return null;
- }).when(driver).loadSoundModel_2_1(modelCaptor.capture(), callbackCaptor.capture(),
- cookieCaptor.capture(), any());
-
- when(mAudioSessionProvider.acquireSession()).thenReturn(
- new SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession(101, 102, 103));
-
- int handle = module.loadModel(model);
- verify(driver).loadSoundModel_2_1(any(), any(), anyInt(), any());
- verify(mAudioSessionProvider).acquireSession();
-
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel hidlModel =
- modelCaptor.getValue();
- assertEquals(android.hardware.soundtrigger.V2_0.SoundModelType.GENERIC,
- hidlModel.header.type);
- assertEquals(model.uuid, ConversionUtil.hidl2aidlUuid(hidlModel.header.uuid));
- assertEquals(model.vendorUuid, ConversionUtil.hidl2aidlUuid(hidlModel.header.vendorUuid));
- assertArrayEquals(new byte[]{91, 92, 93, 94, 95},
- HidlMemoryUtil.hidlMemoryToByteArray(hidlModel.data));
-
- return new Pair<>(handle,
- new SoundTriggerHwCallback(callbackCaptor.getValue(), cookieCaptor.getValue()));
- }
-
private Pair<Integer, SoundTriggerHwCallback> loadGenericModel(ISoundTriggerModule module,
int hwHandle) throws RemoteException {
- if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
- return loadGenericModel_2_1(module, hwHandle);
- } else {
- return loadGenericModel_2_0(module, hwHandle);
- }
+ SoundModel model = TestUtil.createGenericSoundModel();
+ ArgumentCaptor<SoundModel> modelCaptor = ArgumentCaptor.forClass(SoundModel.class);
+ ArgumentCaptor<ISoundTriggerHal.ModelCallback> callbackCaptor = ArgumentCaptor.forClass(
+ ISoundTriggerHal.ModelCallback.class);
+
+ when(mHalDriver.loadSoundModel(any(), any())).thenReturn(hwHandle);
+ when(mAudioSessionProvider.acquireSession()).thenReturn(
+ new SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession(101, 102, 103));
+
+ int handle = module.loadModel(model);
+ verify(mHalDriver).loadSoundModel(modelCaptor.capture(), callbackCaptor.capture());
+ verify(mAudioSessionProvider).acquireSession();
+ assertEquals(model, modelCaptor.getValue());
+ return new Pair<>(handle, new SoundTriggerHwCallback(callbackCaptor.getValue()));
}
- private Pair<Integer, SoundTriggerHwCallback> loadPhraseModel_2_0(ISoundTriggerModule module,
+ private Pair<Integer, SoundTriggerHwCallback> loadPhraseModel(ISoundTriggerModule module,
int hwHandle) throws RemoteException {
- PhraseSoundModel model = createPhraseSoundModel();
- ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel>
- modelCaptor = ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel.class);
- ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback> callbackCaptor =
- ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.class);
- ArgumentCaptor<Integer> cookieCaptor = ArgumentCaptor.forClass(Integer.class);
+ PhraseSoundModel model = TestUtil.createPhraseSoundModel();
+ ArgumentCaptor<PhraseSoundModel> modelCaptor = ArgumentCaptor.forClass(
+ PhraseSoundModel.class);
+ ArgumentCaptor<ISoundTriggerHal.ModelCallback> callbackCaptor = ArgumentCaptor.forClass(
+ ISoundTriggerHal.ModelCallback.class);
- doAnswer(invocation -> {
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback callback =
- invocation.getArgument(
- 1);
- int callbackCookie = invocation.getArgument(2);
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw.loadPhraseSoundModelCallback
- resultCallback =
- invocation.getArgument(
- 3);
-
- // This is the return of this method.
- resultCallback.onValues(0, hwHandle);
-
- // This is the async mCallback that comes after.
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.ModelEvent modelEvent =
- new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.ModelEvent();
- modelEvent.status =
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.SoundModelStatus.UPDATED;
- modelEvent.model = hwHandle;
- callback.soundModelCallback(modelEvent, callbackCookie);
- return null;
- }).when(mHalDriver).loadPhraseSoundModel(modelCaptor.capture(), callbackCaptor.capture(),
- cookieCaptor.capture(), any());
-
+ when(mHalDriver.loadPhraseSoundModel(any(), any())).thenReturn(hwHandle);
when(mAudioSessionProvider.acquireSession()).thenReturn(
new SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession(101, 102, 103));
int handle = module.loadPhraseModel(model);
- verify(mHalDriver).loadPhraseSoundModel(any(), any(), anyInt(), any());
+ verify(mHalDriver).loadPhraseSoundModel(modelCaptor.capture(), callbackCaptor.capture());
verify(mAudioSessionProvider).acquireSession();
-
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel hidlModel =
- modelCaptor.getValue();
-
- // Validate common part.
- assertEquals(android.hardware.soundtrigger.V2_0.SoundModelType.KEYPHRASE,
- hidlModel.common.type);
- assertEquals(model.common.uuid, ConversionUtil.hidl2aidlUuid(hidlModel.common.uuid));
- assertEquals(model.common.vendorUuid,
- ConversionUtil.hidl2aidlUuid(hidlModel.common.vendorUuid));
- assertArrayEquals(new Byte[]{91, 92, 93, 94, 95}, hidlModel.common.data.toArray());
-
- // Validate phrase part.
- assertEquals(1, hidlModel.phrases.size());
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Phrase hidlPhrase =
- hidlModel.phrases.get(0);
- assertEquals(123, hidlPhrase.id);
- assertArrayEquals(new Integer[]{5, 6, 7}, hidlPhrase.users.toArray());
- assertEquals("locale", hidlPhrase.locale);
- assertEquals("text", hidlPhrase.text);
- assertEquals(android.hardware.soundtrigger.V2_0.RecognitionMode.USER_AUTHENTICATION
- | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_IDENTIFICATION,
- hidlPhrase.recognitionModes);
-
- return new Pair<>(handle,
- new SoundTriggerHwCallback(callbackCaptor.getValue(), cookieCaptor.getValue()));
- }
-
- private Pair<Integer, SoundTriggerHwCallback> loadPhraseModel_2_1(ISoundTriggerModule module,
- int hwHandle) throws RemoteException {
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver =
- (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
-
- PhraseSoundModel model = createPhraseSoundModel();
- ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel>
- modelCaptor = ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel.class);
- ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
- ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
- ArgumentCaptor<Integer> cookieCaptor = ArgumentCaptor.forClass(Integer.class);
-
- doAnswer(invocation -> {
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback callback =
- invocation.getArgument(
- 1);
- int callbackCookie = invocation.getArgument(2);
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadPhraseSoundModel_2_1Callback
- resultCallback =
- invocation.getArgument(
- 3);
-
- // This is the return of this method.
- resultCallback.onValues(0, hwHandle);
-
- // This is the async mCallback that comes after.
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.ModelEvent modelEvent =
- new android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.ModelEvent();
- modelEvent.header.status =
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.SoundModelStatus.UPDATED;
- modelEvent.header.model = hwHandle;
- callback.soundModelCallback_2_1(modelEvent, callbackCookie);
- return null;
- }).when(driver).loadPhraseSoundModel_2_1(modelCaptor.capture(), callbackCaptor.capture(),
- cookieCaptor.capture(), any());
-
- when(mAudioSessionProvider.acquireSession()).thenReturn(
- new SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession(101, 102, 103));
-
- int handle = module.loadPhraseModel(model);
- verify(driver).loadPhraseSoundModel_2_1(any(), any(), anyInt(), any());
- verify(mAudioSessionProvider).acquireSession();
-
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel hidlModel =
- modelCaptor.getValue();
-
- // Validate common part.
- assertEquals(android.hardware.soundtrigger.V2_0.SoundModelType.KEYPHRASE,
- hidlModel.common.header.type);
- assertEquals(model.common.uuid, ConversionUtil.hidl2aidlUuid(hidlModel.common.header.uuid));
- assertEquals(model.common.vendorUuid,
- ConversionUtil.hidl2aidlUuid(hidlModel.common.header.vendorUuid));
- assertArrayEquals(new byte[]{91, 92, 93, 94, 95},
- HidlMemoryUtil.hidlMemoryToByteArray(hidlModel.common.data));
-
- // Validate phrase part.
- assertEquals(1, hidlModel.phrases.size());
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.Phrase hidlPhrase =
- hidlModel.phrases.get(0);
- assertEquals(123, hidlPhrase.id);
- assertArrayEquals(new Integer[]{5, 6, 7}, hidlPhrase.users.toArray());
- assertEquals("locale", hidlPhrase.locale);
- assertEquals("text", hidlPhrase.text);
- assertEquals(android.hardware.soundtrigger.V2_0.RecognitionMode.USER_AUTHENTICATION
- | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_IDENTIFICATION,
- hidlPhrase.recognitionModes);
-
- return new Pair<>(handle,
- new SoundTriggerHwCallback(callbackCaptor.getValue(), cookieCaptor.getValue()));
- }
-
- private Pair<Integer, SoundTriggerHwCallback> loadPhraseModel(
- ISoundTriggerModule module, int hwHandle) throws RemoteException {
- if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
- return loadPhraseModel_2_1(module, hwHandle);
- } else {
- return loadPhraseModel_2_0(module, hwHandle);
- }
+ assertEquals(model, modelCaptor.getValue());
+ return new Pair<>(handle, new SoundTriggerHwCallback(callbackCaptor.getValue()));
}
private void unloadModel(ISoundTriggerModule module, int handle, int hwHandle)
@@ -631,204 +111,35 @@
verify(mAudioSessionProvider).releaseSession(101);
}
- private void startRecognition_2_0(ISoundTriggerModule module, int handle,
- int hwHandle) throws RemoteException {
- ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig>
- configCaptor = ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig.class);
+ private void startRecognition(ISoundTriggerModule module, int handle, int hwHandle)
+ throws RemoteException {
+ ArgumentCaptor<RecognitionConfig> configCaptor = ArgumentCaptor.forClass(
+ RecognitionConfig.class);
- when(mHalDriver.startRecognition(eq(hwHandle), configCaptor.capture(), any(), anyInt()))
- .thenReturn(0);
-
- RecognitionConfig config = createRecognitionConfig();
+ RecognitionConfig config = TestUtil.createRecognitionConfig();
module.startRecognition(handle, config);
- verify(mHalDriver).startRecognition(eq(hwHandle), any(), any(), anyInt());
-
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig halConfig =
- configCaptor.getValue();
- assertTrue(halConfig.captureRequested);
- assertEquals(102, halConfig.captureHandle);
- assertEquals(103, halConfig.captureDevice);
- assertEquals(1, halConfig.phrases.size());
- android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halPhraseExtra =
- halConfig.phrases.get(0);
- assertEquals(123, halPhraseExtra.id);
- assertEquals(4, halPhraseExtra.confidenceLevel);
- assertEquals(5, halPhraseExtra.recognitionModes);
- assertEquals(1, halPhraseExtra.levels.size());
- android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel = halPhraseExtra.levels.get(0);
- assertEquals(234, halLevel.userId);
- assertEquals(34, halLevel.levelPercent);
- assertArrayEquals(new Byte[]{5, 4, 3, 2, 1}, halConfig.data.toArray());
- }
-
- private void startRecognition_2_1(ISoundTriggerModule module, int handle,
- int hwHandle) throws RemoteException {
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver =
- (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
-
- ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig>
- configCaptor = ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig.class);
-
- when(driver.startRecognition_2_1(eq(hwHandle), configCaptor.capture(), any(), anyInt()))
- .thenReturn(0);
-
- RecognitionConfig config = createRecognitionConfig();
-
- module.startRecognition(handle, config);
- verify(driver).startRecognition_2_1(eq(hwHandle), any(), any(), anyInt());
-
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig halConfig =
- configCaptor.getValue();
- assertTrue(halConfig.header.captureRequested);
- assertEquals(102, halConfig.header.captureHandle);
- assertEquals(103, halConfig.header.captureDevice);
- assertEquals(1, halConfig.header.phrases.size());
- android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halPhraseExtra =
- halConfig.header.phrases.get(0);
- assertEquals(123, halPhraseExtra.id);
- assertEquals(4, halPhraseExtra.confidenceLevel);
- assertEquals(5, halPhraseExtra.recognitionModes);
- assertEquals(1, halPhraseExtra.levels.size());
- android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel = halPhraseExtra.levels.get(0);
- assertEquals(234, halLevel.userId);
- assertEquals(34, halLevel.levelPercent);
- assertArrayEquals(new byte[]{5, 4, 3, 2, 1},
- HidlMemoryUtil.hidlMemoryToByteArray(halConfig.data));
- }
-
- private void startRecognition_2_3(ISoundTriggerModule module, int handle,
- int hwHandle) throws RemoteException {
- android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
- (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
-
- ArgumentCaptor<android.hardware.soundtrigger.V2_3.RecognitionConfig>
- configCaptor = ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_3.RecognitionConfig.class);
-
- when(driver.startRecognition_2_3(eq(hwHandle), configCaptor.capture())).thenReturn(0);
-
- RecognitionConfig config = createRecognitionConfig();
-
- module.startRecognition(handle, config);
- verify(driver).startRecognition_2_3(eq(hwHandle), any());
-
- android.hardware.soundtrigger.V2_3.RecognitionConfig halConfigExtended =
- configCaptor.getValue();
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig halConfig_2_1 =
- halConfigExtended.base;
-
- assertTrue(halConfig_2_1.header.captureRequested);
- assertEquals(102, halConfig_2_1.header.captureHandle);
- assertEquals(103, halConfig_2_1.header.captureDevice);
- assertEquals(1, halConfig_2_1.header.phrases.size());
- android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halPhraseExtra =
- halConfig_2_1.header.phrases.get(0);
- assertEquals(123, halPhraseExtra.id);
- assertEquals(4, halPhraseExtra.confidenceLevel);
- assertEquals(5, halPhraseExtra.recognitionModes);
- assertEquals(1, halPhraseExtra.levels.size());
- android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel = halPhraseExtra.levels.get(0);
- assertEquals(234, halLevel.userId);
- assertEquals(34, halLevel.levelPercent);
- assertArrayEquals(new byte[]{5, 4, 3, 2, 1},
- HidlMemoryUtil.hidlMemoryToByteArray(halConfig_2_1.data));
- assertEquals(AudioCapabilities.ECHO_CANCELLATION
- | AudioCapabilities.NOISE_SUPPRESSION, halConfigExtended.audioCapabilities);
- }
-
- private void startRecognition(ISoundTriggerModule module, int handle,
- int hwHandle) throws RemoteException {
- if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
- startRecognition_2_3(module, handle, hwHandle);
- } else if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
- startRecognition_2_1(module, handle, hwHandle);
- } else {
- startRecognition_2_0(module, handle, hwHandle);
- }
- }
-
- private RecognitionConfig createRecognitionConfig() {
- RecognitionConfig config = new RecognitionConfig();
- config.captureRequested = true;
- config.phraseRecognitionExtras = new PhraseRecognitionExtra[]{new PhraseRecognitionExtra()};
- config.phraseRecognitionExtras[0].id = 123;
- config.phraseRecognitionExtras[0].confidenceLevel = 4;
- config.phraseRecognitionExtras[0].recognitionModes = 5;
- config.phraseRecognitionExtras[0].levels = new ConfidenceLevel[]{new ConfidenceLevel()};
- config.phraseRecognitionExtras[0].levels[0].userId = 234;
- config.phraseRecognitionExtras[0].levels[0].levelPercent = 34;
- config.data = new byte[]{5, 4, 3, 2, 1};
- config.audioCapabilities = AudioCapabilities.ECHO_CANCELLATION
- | AudioCapabilities.NOISE_SUPPRESSION;
- return config;
+ verify(mHalDriver).startRecognition(eq(hwHandle), eq(103), eq(102), configCaptor.capture());
+ assertEquals(config, configCaptor.getValue());
}
private void stopRecognition(ISoundTriggerModule module, int handle, int hwHandle)
throws RemoteException {
- when(mHalDriver.stopRecognition(hwHandle)).thenReturn(0);
module.stopRecognition(handle);
verify(mHalDriver).stopRecognition(hwHandle);
}
- private void verifyNotStartRecognition() throws RemoteException {
- verify(mHalDriver, never()).startRecognition(anyInt(), any(), any(), anyInt());
- if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
- verify((android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver,
- never()).startRecognition_2_1(anyInt(), any(), any(), anyInt());
- } else if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
- verify((android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver,
- never()).startRecognition_2_3(anyInt(), any());
- }
- }
-
-
@Before
public void setUp() throws Exception {
clearInvocations(mHalDriver);
clearInvocations(mAudioSessionProvider);
+ when(mHalDriver.getProperties()).thenReturn(TestUtil.createDefaultProperties(false));
+ mService = new SoundTriggerMiddlewareImpl(() -> mHalDriver, mAudioSessionProvider);
+ }
- // This binder is associated with the mock, so it can be cast to either version of the
- // HAL interface.
- final IHwBinder binder = new IHwBinder() {
- @Override
- public void transact(int code, HwParcel request, HwParcel reply, int flags)
- throws RemoteException {
- // This is a little hacky, but a very easy way to gracefully reject a request for
- // an unsupported interface (after queryLocalInterface() returns null, the client
- // will attempt a remote transaction to obtain the interface. RemoteException will
- // cause it to give up).
- throw new RemoteException();
- }
-
- @Override
- public IHwInterface queryLocalInterface(String descriptor) {
- if (descriptor.equals("android.hardware.soundtrigger@2.0::ISoundTriggerHw")
- || descriptor.equals("android.hardware.soundtrigger@2.1::ISoundTriggerHw")
- && mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw
- || descriptor.equals("android.hardware.soundtrigger@2.2::ISoundTriggerHw")
- && mHalDriver instanceof android.hardware.soundtrigger.V2_2.ISoundTriggerHw
- || descriptor.equals("android.hardware.soundtrigger@2.3::ISoundTriggerHw")
- && mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
- return mHalDriver;
- }
- return null;
- }
-
- @Override
- public boolean linkToDeath(DeathRecipient recipient, long cookie) {
- return true;
- }
-
- @Override
- public boolean unlinkToDeath(DeathRecipient recipient) {
- return true;
- }
- };
-
- when(mHalDriver.asBinder()).thenReturn(binder);
+ @After
+ public void tearDown() {
+ verify(mHalDriver, never()).reboot();
}
@Test
@@ -836,58 +147,28 @@
}
@Test
- public void testListModules() throws Exception {
- initService(true);
+ public void testListModules() {
// Note: input and output properties are NOT the same type, even though they are in any way
// equivalent. One is a type that's exposed by the HAL and one is a type that's exposed by
// the service. The service actually performs a (trivial) conversion between the two.
SoundTriggerModuleDescriptor[] allDescriptors = mService.listModules();
assertEquals(1, allDescriptors.length);
- SoundTriggerModuleProperties properties = allDescriptors[0].properties;
-
- validateDefaultProperties(properties, true);
- verifyNotGetProperties();
+ Properties properties = allDescriptors[0].properties;
+ assertEquals(TestUtil.createDefaultProperties(false), properties);
}
@Test
public void testAttachDetach() throws Exception {
// Normal attachment / detachment.
- initService(true);
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
- verify(callback).onRecognitionAvailabilityChange(true);
- assertNotNull(module);
- module.detach();
- }
-
- @Test
- public void testAttachDetachNotAvailable() throws Exception {
- // Attachment / detachment during external capture, with a module not supporting concurrent
- // capture.
- initService(false);
- ISoundTriggerCallback callback = createCallbackMock();
- ISoundTriggerModule module = mService.attach(0, callback);
- verify(callback).onRecognitionAvailabilityChange(false);
- assertNotNull(module);
- module.detach();
- }
-
- @Test
- public void testAttachDetachAvailable() throws Exception {
- // Attachment / detachment during external capture, with a module supporting concurrent
- // capture.
- initService(true);
- ISoundTriggerCallback callback = createCallbackMock();
- ISoundTriggerModule module = mService.attach(0, callback);
- verify(callback).onRecognitionAvailabilityChange(true);
assertNotNull(module);
module.detach();
}
@Test
public void testLoadUnloadModel() throws Exception {
- initService(true);
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
@@ -898,8 +179,26 @@
}
@Test
+ public void testLoadPreemptModel() throws Exception {
+ ISoundTriggerCallback callback = createCallbackMock();
+ ISoundTriggerModule module = mService.attach(0, callback);
+
+ final int hwHandle = 7;
+ Pair<Integer, SoundTriggerHwCallback> loadResult = loadGenericModel(module, hwHandle);
+
+ int handle = loadResult.first;
+ SoundTriggerHwCallback hwCallback = loadResult.second;
+
+ // Signal preemption.
+ hwCallback.sendUnloadEvent(hwHandle);
+
+ verify(callback).onModelUnloaded(handle);
+
+ module.detach();
+ }
+
+ @Test
public void testLoadUnloadPhraseModel() throws Exception {
- initService(true);
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
@@ -911,7 +210,6 @@
@Test
public void testStartStopRecognition() throws Exception {
- initService(true);
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
@@ -931,8 +229,31 @@
}
@Test
+ public void testStartRecognitionBusy() throws Exception {
+ ISoundTriggerCallback callback = createCallbackMock();
+ ISoundTriggerModule module = mService.attach(0, callback);
+
+ // Load the model.
+ final int hwHandle = 7;
+ int handle = loadGenericModel(module, hwHandle).first;
+
+ // Start the model.
+ doThrow(new RecoverableException(Status.RESOURCE_CONTENTION)).when(
+ mHalDriver).startRecognition(eq(7), eq(103), eq(102), any());
+
+ try {
+ RecognitionConfig config = TestUtil.createRecognitionConfig();
+ module.startRecognition(handle, config);
+ fail("Expected an exception");
+ } catch (RecoverableException e) {
+ assertEquals(Status.RESOURCE_CONTENTION, e.errorCode);
+ }
+
+ verify(mHalDriver).startRecognition(eq(7), eq(103), eq(102), any());
+ }
+
+ @Test
public void testStartStopPhraseRecognition() throws Exception {
- initService(true);
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
@@ -953,7 +274,6 @@
@Test
public void testRecognition() throws Exception {
- initService(true);
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
@@ -967,15 +287,15 @@
startRecognition(module, handle, hwHandle);
// Signal a capture from the driver.
- hwCallback.sendRecognitionEvent(hwHandle,
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.SUCCESS);
+ RecognitionEvent event = hwCallback.sendRecognitionEvent(hwHandle,
+ RecognitionStatus.SUCCESS);
ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
RecognitionEvent.class);
- verify(callback).onRecognition(eq(handle), eventCaptor.capture());
+ verify(callback).onRecognition(eq(handle), eventCaptor.capture(), eq(101));
// Validate the event.
- validateRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.SUCCESS);
+ assertEquals(event, eventCaptor.getValue());
// Unload the model.
unloadModel(module, handle, hwHandle);
@@ -984,7 +304,6 @@
@Test
public void testPhraseRecognition() throws Exception {
- initService(true);
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
@@ -998,15 +317,15 @@
startRecognition(module, handle, hwHandle);
// Signal a capture from the driver.
- hwCallback.sendPhraseRecognitionEvent(hwHandle,
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.SUCCESS);
+ PhraseRecognitionEvent event = hwCallback.sendPhraseRecognitionEvent(hwHandle,
+ RecognitionStatus.SUCCESS);
ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
PhraseRecognitionEvent.class);
- verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture());
+ verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture(), eq(101));
// Validate the event.
- validatePhraseRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.SUCCESS);
+ assertEquals(event, eventCaptor.getValue());
// Unload the model.
unloadModel(module, handle, hwHandle);
@@ -1015,14 +334,6 @@
@Test
public void testForceRecognition() throws Exception {
- if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_2.ISoundTriggerHw)) {
- return;
- }
-
- android.hardware.soundtrigger.V2_2.ISoundTriggerHw driver =
- (android.hardware.soundtrigger.V2_2.ISoundTriggerHw) mHalDriver;
-
- initService(true);
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
@@ -1037,18 +348,49 @@
// Force a trigger.
module.forceRecognitionEvent(handle);
- verify(driver).getModelState(hwHandle);
+ verify(mHalDriver).forceRecognitionEvent(hwHandle);
// Signal a capture from the driver.
- // '3' means 'forced', there's no constant for that in the HAL.
- hwCallback.sendRecognitionEvent(hwHandle, 3);
+ RecognitionEvent event = hwCallback.sendRecognitionEvent(hwHandle,
+ RecognitionStatus.FORCED);
ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
RecognitionEvent.class);
- verify(callback).onRecognition(eq(handle), eventCaptor.capture());
+ verify(callback).onRecognition(eq(handle), eventCaptor.capture(), eq(101));
// Validate the event.
- validateRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.FORCED);
+ assertEquals(event, eventCaptor.getValue());
+
+ // Stop the recognition.
+ stopRecognition(module, handle, hwHandle);
+
+ // Unload the model.
+ unloadModel(module, handle, hwHandle);
+ module.detach();
+ }
+
+ @Test
+ public void testForceRecognitionNotSupported() throws Exception {
+ ISoundTriggerCallback callback = createCallbackMock();
+ ISoundTriggerModule module = mService.attach(0, callback);
+
+ // Load the model.
+ final int hwHandle = 17;
+ Pair<Integer, SoundTriggerHwCallback> modelHandles = loadGenericModel(module, hwHandle);
+ int handle = modelHandles.first;
+
+ // Initiate a recognition.
+ startRecognition(module, handle, hwHandle);
+
+ // Force a trigger.
+ doThrow(new RecoverableException(Status.OPERATION_NOT_SUPPORTED)).when(
+ mHalDriver).forceRecognitionEvent(hwHandle);
+ try {
+ module.forceRecognitionEvent(handle);
+ fail("Expected an exception");
+ } catch (RecoverableException e) {
+ assertEquals(Status.OPERATION_NOT_SUPPORTED, e.errorCode);
+ }
// Stop the recognition.
stopRecognition(module, handle, hwHandle);
@@ -1060,14 +402,6 @@
@Test
public void testForcePhraseRecognition() throws Exception {
- if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_2.ISoundTriggerHw)) {
- return;
- }
-
- android.hardware.soundtrigger.V2_2.ISoundTriggerHw driver =
- (android.hardware.soundtrigger.V2_2.ISoundTriggerHw) mHalDriver;
-
- initService(true);
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
@@ -1082,18 +416,49 @@
// Force a trigger.
module.forceRecognitionEvent(handle);
- verify(driver).getModelState(hwHandle);
+ verify(mHalDriver).forceRecognitionEvent(hwHandle);
// Signal a capture from the driver.
- // '3' means 'forced', there's no constant for that in the HAL.
- hwCallback.sendPhraseRecognitionEvent(hwHandle, 3);
+ PhraseRecognitionEvent event = hwCallback.sendPhraseRecognitionEvent(hwHandle,
+ RecognitionStatus.FORCED);
ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
PhraseRecognitionEvent.class);
- verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture());
+ verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture(), eq(101));
// Validate the event.
- validatePhraseRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.FORCED);
+ assertEquals(event, eventCaptor.getValue());
+
+ // Stop the recognition.
+ stopRecognition(module, handle, hwHandle);
+
+ // Unload the model.
+ unloadModel(module, handle, hwHandle);
+ module.detach();
+ }
+
+ @Test
+ public void testForcePhraseRecognitionNotSupported() throws Exception {
+ ISoundTriggerCallback callback = createCallbackMock();
+ ISoundTriggerModule module = mService.attach(0, callback);
+
+ // Load the model.
+ final int hwHandle = 17;
+ Pair<Integer, SoundTriggerHwCallback> modelHandles = loadPhraseModel(module, hwHandle);
+ int handle = modelHandles.first;
+
+ // Initiate a recognition.
+ startRecognition(module, handle, hwHandle);
+
+ // Force a trigger.
+ doThrow(new RecoverableException(Status.OPERATION_NOT_SUPPORTED)).when(
+ mHalDriver).forceRecognitionEvent(hwHandle);
+ try {
+ module.forceRecognitionEvent(handle);
+ fail("Expected an exception");
+ } catch (RecoverableException e) {
+ assertEquals(Status.OPERATION_NOT_SUPPORTED, e.errorCode);
+ }
// Stop the recognition.
stopRecognition(module, handle, hwHandle);
@@ -1106,46 +471,28 @@
@Test
public void testAbortRecognition() throws Exception {
// Make sure the HAL doesn't support concurrent capture.
- initService(false);
- mService.setCaptureState(false);
-
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
- verify(callback).onRecognitionAvailabilityChange(true);
// Load the model.
final int hwHandle = 11;
- int handle = loadGenericModel(module, hwHandle).first;
+ Pair<Integer, SoundTriggerHwCallback> loadResult = loadGenericModel(module, hwHandle);
+ int handle = loadResult.first;
+ SoundTriggerHwCallback hwCallback = loadResult.second;
// Initiate a recognition.
startRecognition(module, handle, hwHandle);
// Abort.
- mService.setCaptureState(true);
+ hwCallback.sendRecognitionEvent(hwHandle, RecognitionStatus.ABORTED);
ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
RecognitionEvent.class);
- verify(callback).onRecognition(eq(handle), eventCaptor.capture());
+ verify(callback).onRecognition(eq(handle), eventCaptor.capture(), eq(101));
// Validate the event.
assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().status);
- // Make sure we are notified of the lost availability.
- verify(callback).onRecognitionAvailabilityChange(false);
-
- // Attempt to start a new recognition - should get an abort event immediately, without
- // involving the HAL.
- clearInvocations(callback);
- clearInvocations(mHalDriver);
- module.startRecognition(handle, createRecognitionConfig());
- verify(callback).onRecognition(eq(handle), eventCaptor.capture());
- assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().status);
- verifyNotStartRecognition();
-
- // Now enable it and make sure we are notified.
- mService.setCaptureState(false);
- verify(callback).onRecognitionAvailabilityChange(true);
-
// Unload the model.
unloadModel(module, handle, hwHandle);
module.detach();
@@ -1154,298 +501,124 @@
@Test
public void testAbortPhraseRecognition() throws Exception {
// Make sure the HAL doesn't support concurrent capture.
- initService(false);
- mService.setCaptureState(false);
-
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
- verify(callback).onRecognitionAvailabilityChange(true);
// Load the model.
final int hwHandle = 11;
- int handle = loadPhraseModel(module, hwHandle).first;
+ Pair<Integer, SoundTriggerHwCallback> loadResult = loadPhraseModel(module, hwHandle);
+ int handle = loadResult.first;
+ SoundTriggerHwCallback hwCallback = loadResult.second;
// Initiate a recognition.
startRecognition(module, handle, hwHandle);
// Abort.
- mService.setCaptureState(true);
+ hwCallback.sendPhraseRecognitionEvent(hwHandle, RecognitionStatus.ABORTED);
ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
PhraseRecognitionEvent.class);
- verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture());
+ verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture(), eq(101));
// Validate the event.
assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().common.status);
- // Make sure we are notified of the lost availability.
- verify(callback).onRecognitionAvailabilityChange(false);
-
- // Attempt to start a new recognition - should get an abort event immediately, without
- // involving the HAL.
- clearInvocations(callback);
- clearInvocations(mHalDriver);
- module.startRecognition(handle, createRecognitionConfig());
- verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture());
- assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().common.status);
- verifyNotStartRecognition();
-
- // Now enable it and make sure we are notified.
- mService.setCaptureState(false);
- verify(callback).onRecognitionAvailabilityChange(true);
-
// Unload the model.
unloadModel(module, handle, hwHandle);
module.detach();
}
@Test
- public void testNotAbortRecognitionConcurrent() throws Exception {
- // Make sure the HAL supports concurrent capture.
- initService(true);
-
- ISoundTriggerCallback callback = createCallbackMock();
- ISoundTriggerModule module = mService.attach(0, callback);
- verify(callback).onRecognitionAvailabilityChange(true);
- clearInvocations(callback);
-
- // Load the model.
- final int hwHandle = 13;
- int handle = loadGenericModel(module, hwHandle).first;
-
- // Initiate a recognition.
- startRecognition(module, handle, hwHandle);
-
- // Signal concurrent capture. Shouldn't abort.
- mService.setCaptureState(true);
- verify(callback, never()).onRecognition(anyInt(), any());
- verify(callback, never()).onRecognitionAvailabilityChange(anyBoolean());
-
- // Stop the recognition.
- stopRecognition(module, handle, hwHandle);
-
- // Initiating a new one should work fine.
- clearInvocations(mHalDriver);
- startRecognition(module, handle, hwHandle);
- verify(callback, never()).onRecognition(anyInt(), any());
- stopRecognition(module, handle, hwHandle);
-
- // Unload the model.
- module.unloadModel(handle);
- module.detach();
- }
-
- @Test
- public void testNotAbortPhraseRecognitionConcurrent() throws Exception {
- // Make sure the HAL supports concurrent capture.
- initService(true);
-
- ISoundTriggerCallback callback = createCallbackMock();
- ISoundTriggerModule module = mService.attach(0, callback);
- verify(callback).onRecognitionAvailabilityChange(true);
- clearInvocations(callback);
-
- // Load the model.
- final int hwHandle = 13;
- int handle = loadPhraseModel(module, hwHandle).first;
-
- // Initiate a recognition.
- startRecognition(module, handle, hwHandle);
-
- // Signal concurrent capture. Shouldn't abort.
- mService.setCaptureState(true);
- verify(callback, never()).onPhraseRecognition(anyInt(), any());
- verify(callback, never()).onRecognitionAvailabilityChange(anyBoolean());
-
- // Stop the recognition.
- stopRecognition(module, handle, hwHandle);
-
- // Initiating a new one should work fine.
- clearInvocations(mHalDriver);
- startRecognition(module, handle, hwHandle);
- verify(callback, never()).onRecognition(anyInt(), any());
- stopRecognition(module, handle, hwHandle);
-
- // Unload the model.
- module.unloadModel(handle);
- module.detach();
- }
-
- @Test
public void testParameterSupported() throws Exception {
- if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw)) {
- return;
- }
-
- android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
- (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
-
- initService(false);
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
final int hwHandle = 12;
int modelHandle = loadGenericModel(module, hwHandle).first;
- doAnswer((Answer<Void>) invocation -> {
- android.hardware.soundtrigger.V2_3.ISoundTriggerHw.queryParameterCallback
- resultCallback = invocation.getArgument(2);
- android.hardware.soundtrigger.V2_3.ModelParameterRange range =
- new android.hardware.soundtrigger.V2_3.ModelParameterRange();
- range.start = 23;
- range.end = 45;
- OptionalModelParameterRange optionalRange = new OptionalModelParameterRange();
- optionalRange.range(range);
- resultCallback.onValues(0, optionalRange);
- return null;
- }).when(driver).queryParameter(eq(hwHandle),
- eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
+ ModelParameterRange halRange = new ModelParameterRange();
+ halRange.minInclusive = 23;
+ halRange.maxInclusive = 45;
+
+ when(mHalDriver.queryParameter(eq(hwHandle),
+ eq(ModelParameter.THRESHOLD_FACTOR))).thenReturn(halRange);
ModelParameterRange range = module.queryModelParameterSupport(modelHandle,
ModelParameter.THRESHOLD_FACTOR);
- verify(driver).queryParameter(eq(hwHandle),
- eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
+ verify(mHalDriver).queryParameter(eq(hwHandle), eq(ModelParameter.THRESHOLD_FACTOR));
- assertEquals(23, range.minInclusive);
- assertEquals(45, range.maxInclusive);
- }
-
- @Test
- public void testParameterNotSupportedOld() throws Exception {
- if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
- return;
- }
-
- initService(false);
- ISoundTriggerCallback callback = createCallbackMock();
- ISoundTriggerModule module = mService.attach(0, callback);
- final int hwHandle = 13;
- int modelHandle = loadGenericModel(module, hwHandle).first;
-
- ModelParameterRange range = module.queryModelParameterSupport(modelHandle,
- ModelParameter.THRESHOLD_FACTOR);
-
- assertNull(range);
+ assertEquals(halRange, range);
}
@Test
public void testParameterNotSupported() throws Exception {
- if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw)) {
- return;
- }
-
- android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
- (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
-
- initService(false);
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
final int hwHandle = 13;
int modelHandle = loadGenericModel(module, hwHandle).first;
- doAnswer(invocation -> {
- android.hardware.soundtrigger.V2_3.ISoundTriggerHw.queryParameterCallback
- resultCallback = invocation.getArgument(2);
- // This is the return of this method.
- resultCallback.onValues(0, new OptionalModelParameterRange());
- return null;
- }).when(driver).queryParameter(eq(hwHandle),
- eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
+ when(mHalDriver.queryParameter(eq(hwHandle),
+ eq(ModelParameter.THRESHOLD_FACTOR))).thenReturn(null);
ModelParameterRange range = module.queryModelParameterSupport(modelHandle,
ModelParameter.THRESHOLD_FACTOR);
- verify(driver).queryParameter(eq(hwHandle),
- eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
+ verify(mHalDriver).queryParameter(eq(hwHandle), eq(ModelParameter.THRESHOLD_FACTOR));
assertNull(range);
}
@Test
public void testGetParameter() throws Exception {
- if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw)) {
- return;
- }
-
- android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
- (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
-
- initService(false);
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
final int hwHandle = 14;
int modelHandle = loadGenericModel(module, hwHandle).first;
- doAnswer(invocation -> {
- android.hardware.soundtrigger.V2_3.ISoundTriggerHw.getParameterCallback
- resultCallback = invocation.getArgument(2);
- // This is the return of this method.
- resultCallback.onValues(0, 234);
- return null;
- }).when(driver).getParameter(eq(hwHandle),
- eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
+ when(mHalDriver.getModelParameter(hwHandle, ModelParameter.THRESHOLD_FACTOR)).thenReturn(
+ 234);
int value = module.getModelParameter(modelHandle, ModelParameter.THRESHOLD_FACTOR);
- verify(driver).getParameter(eq(hwHandle),
- eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
+ verify(mHalDriver).getModelParameter(hwHandle, ModelParameter.THRESHOLD_FACTOR);
assertEquals(234, value);
}
@Test
public void testSetParameter() throws Exception {
- if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw)) {
- return;
- }
-
- android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
- (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
-
- initService(false);
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
final int hwHandle = 17;
int modelHandle = loadGenericModel(module, hwHandle).first;
- when(driver.setParameter(hwHandle,
- android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR,
- 456)).thenReturn(0);
-
module.setModelParameter(modelHandle, ModelParameter.THRESHOLD_FACTOR, 456);
- verify(driver).setParameter(hwHandle,
- android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR, 456);
+ verify(mHalDriver).setModelParameter(hwHandle, ModelParameter.THRESHOLD_FACTOR, 456);
}
private static class SoundTriggerHwCallback {
- private final android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback mCallback;
- private final int mCookie;
+ private final ISoundTriggerHal.ModelCallback mCallback;
- SoundTriggerHwCallback(android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback callback,
- int cookie) {
+ SoundTriggerHwCallback(ISoundTriggerHal.ModelCallback callback) {
mCallback = callback;
- mCookie = cookie;
}
- private void sendRecognitionEvent(int hwHandle, int status) throws RemoteException {
- if (mCallback instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback) {
- ((android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback) mCallback).recognitionCallback_2_1(
- createRecognitionEvent_2_1(hwHandle, status), mCookie);
- } else {
- mCallback.recognitionCallback(createRecognitionEvent_2_0(hwHandle, status),
- mCookie);
- }
+ private RecognitionEvent sendRecognitionEvent(int hwHandle, @RecognitionStatus int status) {
+ RecognitionEvent event = TestUtil.createRecognitionEvent(status);
+ mCallback.recognitionCallback(hwHandle, event);
+ return event;
}
- private void sendPhraseRecognitionEvent(int hwHandle, int status) throws RemoteException {
- if (mCallback instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback) {
- ((android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback) mCallback).phraseRecognitionCallback_2_1(
- createPhraseRecognitionEvent_2_1(hwHandle, status), mCookie);
- } else {
- mCallback.phraseRecognitionCallback(
- createPhraseRecognitionEvent_2_0(hwHandle, status), mCookie);
- }
+ private PhraseRecognitionEvent sendPhraseRecognitionEvent(int hwHandle,
+ @RecognitionStatus int status) {
+ PhraseRecognitionEvent event = TestUtil.createPhraseRecognitionEvent(status);
+ mCallback.phraseRecognitionCallback(hwHandle, event);
+ return event;
+ }
+
+ private void sendUnloadEvent(int hwHandle) {
+ mCallback.modelUnloaded(hwHandle);
}
}
}
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java
new file mode 100644
index 0000000..4eca298
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.soundtrigger_middleware;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.annotation.NonNull;
+import android.hardware.soundtrigger.V2_1.ISoundTriggerHw;
+import android.hardware.soundtrigger.V2_4.ISoundTriggerHwCallback;
+import android.media.audio.common.AudioChannelMask;
+import android.media.audio.common.AudioConfig;
+import android.media.audio.common.AudioFormat;
+import android.media.soundtrigger.AudioCapabilities;
+import android.media.soundtrigger.ConfidenceLevel;
+import android.media.soundtrigger.Phrase;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseRecognitionExtra;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionMode;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.SoundModelType;
+import android.os.HidlMemoryUtil;
+import android.os.ParcelFileDescriptor;
+import android.os.SharedMemory;
+import android.system.ErrnoException;
+
+import java.io.FileDescriptor;
+import java.nio.ByteBuffer;
+import java.util.List;
+
+/**
+ * Test utilities, aimed at generating populated objects of the various types and validating
+ * corresponding objects generated by the system under test.
+ */
+class TestUtil {
+ static SoundModel createGenericSoundModel() {
+ return createSoundModel(SoundModelType.GENERIC);
+ }
+
+ private static SoundModel createSoundModel(@SoundModelType int type) {
+ SoundModel model = new SoundModel();
+ model.type = type;
+ model.uuid = "12345678-2345-3456-4567-abcdef987654";
+ model.vendorUuid = "87654321-5432-6543-7654-456789fedcba";
+ byte[] data = new byte[]{91, 92, 93, 94, 95};
+ model.data = new ParcelFileDescriptor(byteArrayToFileDescriptor(data));
+ model.dataSize = data.length;
+ return model;
+ }
+
+ private static void validateSoundModel_2_1(ISoundTriggerHw.SoundModel model, int type) {
+ assertEquals(type, model.header.type);
+ assertEquals("12345678-2345-3456-4567-abcdef987654",
+ ConversionUtil.hidl2aidlUuid(model.header.uuid));
+ assertEquals("87654321-5432-6543-7654-456789fedcba",
+ ConversionUtil.hidl2aidlUuid(model.header.vendorUuid));
+ assertArrayEquals(new byte[]{91, 92, 93, 94, 95},
+ HidlMemoryUtil.hidlMemoryToByteArray(model.data));
+ }
+
+ private static void validateSoundModel_2_0(
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel model, int type) {
+ assertEquals(type, model.type);
+ assertEquals("12345678-2345-3456-4567-abcdef987654",
+ ConversionUtil.hidl2aidlUuid(model.uuid));
+ assertEquals("87654321-5432-6543-7654-456789fedcba",
+ ConversionUtil.hidl2aidlUuid(model.vendorUuid));
+ assertArrayEquals(new Byte[]{91, 92, 93, 94, 95}, model.data.toArray());
+ }
+
+ static void validateGenericSoundModel_2_1(ISoundTriggerHw.SoundModel model) {
+ validateSoundModel_2_1(model, android.hardware.soundtrigger.V2_0.SoundModelType.GENERIC);
+ }
+
+ static void validateGenericSoundModel_2_0(
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel model) {
+ validateSoundModel_2_0(model, android.hardware.soundtrigger.V2_0.SoundModelType.GENERIC);
+ }
+
+ static PhraseSoundModel createPhraseSoundModel() {
+ PhraseSoundModel model = new PhraseSoundModel();
+ model.common = createSoundModel(SoundModelType.KEYPHRASE);
+ model.phrases = new Phrase[1];
+ model.phrases[0] = new Phrase();
+ model.phrases[0].id = 123;
+ model.phrases[0].users = new int[]{5, 6, 7};
+ model.phrases[0].locale = "locale";
+ model.phrases[0].text = "text";
+ model.phrases[0].recognitionModes =
+ RecognitionMode.USER_AUTHENTICATION | RecognitionMode.USER_IDENTIFICATION;
+ return model;
+ }
+
+ static void validatePhraseSoundModel_2_1(ISoundTriggerHw.PhraseSoundModel model) {
+ validateSoundModel_2_1(model.common,
+ android.hardware.soundtrigger.V2_0.SoundModelType.KEYPHRASE);
+ validatePhrases_2_0(model.phrases);
+ }
+
+ static void validatePhraseSoundModel_2_0(
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel model) {
+ validateSoundModel_2_0(model.common,
+ android.hardware.soundtrigger.V2_0.SoundModelType.KEYPHRASE);
+ validatePhrases_2_0(model.phrases);
+ }
+
+ private static void validatePhrases_2_0(
+ List<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Phrase> phrases) {
+ assertEquals(1, phrases.size());
+ assertEquals(123, phrases.get(0).id);
+ assertArrayEquals(new Integer[]{5, 6, 7}, phrases.get(0).users.toArray());
+ assertEquals("locale", phrases.get(0).locale);
+ assertEquals("text", phrases.get(0).text);
+ assertEquals(RecognitionMode.USER_AUTHENTICATION | RecognitionMode.USER_IDENTIFICATION,
+ phrases.get(0).recognitionModes);
+ }
+
+ static android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties createDefaultProperties_2_0(
+ boolean supportConcurrentCapture) {
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties properties =
+ new android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties();
+ properties.implementor = "implementor";
+ properties.description = "description";
+ properties.version = 123;
+ properties.uuid.timeLow = 1;
+ properties.uuid.timeMid = 2;
+ properties.uuid.versionAndTimeHigh = 3;
+ properties.uuid.variantAndClockSeqHigh = 4;
+ properties.uuid.node = new byte[]{5, 6, 7, 8, 9, 10};
+
+ properties.maxSoundModels = 456;
+ properties.maxKeyPhrases = 567;
+ properties.maxUsers = 678;
+ properties.recognitionModes =
+ android.hardware.soundtrigger.V2_0.RecognitionMode.VOICE_TRIGGER
+ | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_IDENTIFICATION
+ | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_AUTHENTICATION
+ | android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER;
+ properties.captureTransition = true;
+ properties.maxBufferMs = 321;
+ properties.concurrentCapture = supportConcurrentCapture;
+ properties.triggerInEvent = true;
+ properties.powerConsumptionMw = 432;
+ return properties;
+ }
+
+ static android.hardware.soundtrigger.V2_3.Properties createDefaultProperties_2_3(
+ boolean supportConcurrentCapture) {
+ android.hardware.soundtrigger.V2_3.Properties properties =
+ new android.hardware.soundtrigger.V2_3.Properties();
+ properties.base = createDefaultProperties_2_0(supportConcurrentCapture);
+ properties.supportedModelArch = "supportedModelArch";
+ properties.audioCapabilities =
+ android.hardware.soundtrigger.V2_3.AudioCapabilities.ECHO_CANCELLATION
+ | android.hardware.soundtrigger.V2_3.AudioCapabilities.NOISE_SUPPRESSION;
+ return properties;
+ }
+
+ static Properties createDefaultProperties(boolean supportConcurrentCapture) {
+ Properties properties = new Properties();
+ properties.implementor = "implementor";
+ properties.description = "description";
+ properties.version = 123;
+ properties.uuid = "00000001-0002-0003-0004-05060708090a";
+ properties.maxSoundModels = 456;
+ properties.maxKeyPhrases = 567;
+ properties.maxUsers = 678;
+ properties.recognitionModes =
+ RecognitionMode.VOICE_TRIGGER
+ | RecognitionMode.USER_IDENTIFICATION
+ | RecognitionMode.USER_AUTHENTICATION
+ | RecognitionMode.GENERIC_TRIGGER;
+ properties.captureTransition = true;
+ properties.maxBufferMs = 321;
+ properties.concurrentCapture = supportConcurrentCapture;
+ properties.triggerInEvent = true;
+ properties.powerConsumptionMw = 432;
+ properties.supportedModelArch = "supportedModelArch";
+ properties.audioCapabilities = AudioCapabilities.ECHO_CANCELLATION
+ | AudioCapabilities.NOISE_SUPPRESSION;
+ return properties;
+ }
+
+ static void validateDefaultProperties(Properties properties,
+ boolean supportConcurrentCapture) {
+ validateDefaultProperties(properties, supportConcurrentCapture,
+ AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION,
+ "supportedModelArch");
+ }
+
+ static void validateDefaultProperties(Properties properties,
+ boolean supportConcurrentCapture, @AudioCapabilities int audioCapabilities,
+ @NonNull String supportedModelArch) {
+ assertEquals("implementor", properties.implementor);
+ assertEquals("description", properties.description);
+ assertEquals(123, properties.version);
+ assertEquals("00000001-0002-0003-0004-05060708090a", properties.uuid);
+ assertEquals(456, properties.maxSoundModels);
+ assertEquals(567, properties.maxKeyPhrases);
+ assertEquals(678, properties.maxUsers);
+ assertEquals(RecognitionMode.GENERIC_TRIGGER
+ | RecognitionMode.USER_AUTHENTICATION
+ | RecognitionMode.USER_IDENTIFICATION
+ | RecognitionMode.VOICE_TRIGGER, properties.recognitionModes);
+ assertTrue(properties.captureTransition);
+ assertEquals(321, properties.maxBufferMs);
+ assertEquals(supportConcurrentCapture, properties.concurrentCapture);
+ assertTrue(properties.triggerInEvent);
+ assertEquals(432, properties.powerConsumptionMw);
+ assertEquals(supportedModelArch, properties.supportedModelArch);
+ assertEquals(audioCapabilities, properties.audioCapabilities);
+ }
+
+ static RecognitionConfig createRecognitionConfig() {
+ RecognitionConfig config = new RecognitionConfig();
+ config.captureRequested = true;
+ config.phraseRecognitionExtras = new PhraseRecognitionExtra[]{new PhraseRecognitionExtra()};
+ config.phraseRecognitionExtras[0].id = 123;
+ config.phraseRecognitionExtras[0].confidenceLevel = 4;
+ config.phraseRecognitionExtras[0].recognitionModes = 5;
+ config.phraseRecognitionExtras[0].levels = new ConfidenceLevel[]{new ConfidenceLevel()};
+ config.phraseRecognitionExtras[0].levels[0].userId = 234;
+ config.phraseRecognitionExtras[0].levels[0].levelPercent = 34;
+ config.data = new byte[]{5, 4, 3, 2, 1};
+ config.audioCapabilities = AudioCapabilities.ECHO_CANCELLATION
+ | AudioCapabilities.NOISE_SUPPRESSION;
+ return config;
+ }
+
+ static void validateRecognitionConfig_2_0(
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig config,
+ int captureDevice, int captureHandle) {
+ assertTrue(config.captureRequested);
+ assertEquals(captureDevice, config.captureDevice);
+ assertEquals(captureHandle, config.captureHandle);
+ assertEquals(1, config.phrases.size());
+ android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halPhraseExtra =
+ config.phrases.get(0);
+ assertEquals(123, halPhraseExtra.id);
+ assertEquals(4, halPhraseExtra.confidenceLevel);
+ assertEquals(5, halPhraseExtra.recognitionModes);
+ assertEquals(1, halPhraseExtra.levels.size());
+ android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel = halPhraseExtra.levels.get(0);
+ assertEquals(234, halLevel.userId);
+ assertEquals(34, halLevel.levelPercent);
+ assertArrayEquals(new Byte[]{5, 4, 3, 2, 1}, config.data.toArray());
+ }
+
+ static void validateRecognitionConfig_2_1(
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig config,
+ int captureDevice, int captureHandle) {
+ assertTrue(config.header.captureRequested);
+ assertEquals(captureDevice, config.header.captureDevice);
+ assertEquals(captureHandle, config.header.captureHandle);
+ assertEquals(1, config.header.phrases.size());
+ android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halPhraseExtra =
+ config.header.phrases.get(0);
+ assertEquals(123, halPhraseExtra.id);
+ assertEquals(4, halPhraseExtra.confidenceLevel);
+ assertEquals(5, halPhraseExtra.recognitionModes);
+ assertEquals(1, halPhraseExtra.levels.size());
+ android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel = halPhraseExtra.levels.get(0);
+ assertEquals(234, halLevel.userId);
+ assertEquals(34, halLevel.levelPercent);
+ assertArrayEquals(new byte[]{5, 4, 3, 2, 1},
+ HidlMemoryUtil.hidlMemoryToByteArray(config.data));
+ }
+
+ static void validateRecognitionConfig_2_3(
+ android.hardware.soundtrigger.V2_3.RecognitionConfig config, int captureDevice,
+ int captureHandle) {
+ validateRecognitionConfig_2_1(config.base, captureDevice, captureHandle);
+
+ assertEquals(AudioCapabilities.ECHO_CANCELLATION
+ | AudioCapabilities.NOISE_SUPPRESSION, config.audioCapabilities);
+ }
+
+ static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_0(
+ int hwHandle,
+ int status) {
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent halEvent =
+ new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent();
+ halEvent.status = status;
+ halEvent.type = SoundModelType.GENERIC;
+ halEvent.model = hwHandle;
+ halEvent.captureAvailable = true;
+ // This field is ignored.
+ halEvent.captureSession = 9999;
+ halEvent.captureDelayMs = 234;
+ halEvent.capturePreambleMs = 345;
+ halEvent.triggerInData = true;
+ halEvent.audioConfig.sampleRateHz = 456;
+ halEvent.audioConfig.channelMask = AudioChannelMask.IN_LEFT;
+ halEvent.audioConfig.format = AudioFormat.MP3;
+ // hwEvent.audioConfig.offloadInfo is irrelevant.
+ halEvent.data.add((byte) 31);
+ halEvent.data.add((byte) 32);
+ halEvent.data.add((byte) 33);
+ return halEvent;
+ }
+
+ static RecognitionEvent createRecognitionEvent(@RecognitionStatus int status) {
+ RecognitionEvent event = new RecognitionEvent();
+ event.status = status;
+ event.type = SoundModelType.GENERIC;
+ event.captureAvailable = true;
+ event.captureDelayMs = 234;
+ event.capturePreambleMs = 345;
+ event.triggerInData = true;
+ event.audioConfig = new AudioConfig();
+ event.audioConfig.sampleRateHz = 456;
+ event.audioConfig.channelMask = AudioChannelMask.IN_LEFT;
+ event.audioConfig.format = AudioFormat.MP3;
+ //event.audioConfig.offloadInfo is irrelevant.
+ event.data = new byte[]{31, 32, 33};
+ return event;
+ }
+
+ static ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_1(
+ int hwHandle,
+ int status) {
+ ISoundTriggerHwCallback.RecognitionEvent halEvent =
+ new ISoundTriggerHwCallback.RecognitionEvent();
+ halEvent.header = createRecognitionEvent_2_0(hwHandle, status);
+ halEvent.header.data.clear();
+ halEvent.data = HidlMemoryUtil.byteArrayToHidlMemory(new byte[]{31, 32, 33});
+ return halEvent;
+ }
+
+ static void validateRecognitionEvent(RecognitionEvent event, @RecognitionStatus int status) {
+ assertEquals(status, event.status);
+ assertEquals(SoundModelType.GENERIC, event.type);
+ assertTrue(event.captureAvailable);
+ assertEquals(234, event.captureDelayMs);
+ assertEquals(345, event.capturePreambleMs);
+ assertTrue(event.triggerInData);
+ assertEquals(456, event.audioConfig.sampleRateHz);
+ assertEquals(AudioChannelMask.IN_LEFT, event.audioConfig.channelMask);
+ assertEquals(AudioFormat.MP3, event.audioConfig.format);
+ assertArrayEquals(new byte[]{31, 32, 33}, event.data);
+ }
+
+ static PhraseRecognitionEvent createPhraseRecognitionEvent(@RecognitionStatus int status) {
+ PhraseRecognitionEvent event = new PhraseRecognitionEvent();
+ event.common = createRecognitionEvent(status);
+
+ PhraseRecognitionExtra extra = new PhraseRecognitionExtra();
+ extra.id = 123;
+ extra.confidenceLevel = 52;
+ extra.recognitionModes = RecognitionMode.VOICE_TRIGGER
+ | RecognitionMode.GENERIC_TRIGGER;
+ ConfidenceLevel level = new ConfidenceLevel();
+ level.userId = 31;
+ level.levelPercent = 43;
+ extra.levels = new ConfidenceLevel[]{level};
+ event.phraseExtras = new PhraseRecognitionExtra[]{extra};
+ return event;
+ }
+
+ static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent
+ createPhraseRecognitionEvent_2_0(int hwHandle, int status) {
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent halEvent =
+ new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent();
+ halEvent.common = createRecognitionEvent_2_0(hwHandle, status);
+
+ android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halExtra =
+ new android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra();
+ halExtra.id = 123;
+ halExtra.confidenceLevel = 52;
+ halExtra.recognitionModes = android.hardware.soundtrigger.V2_0.RecognitionMode.VOICE_TRIGGER
+ | android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER;
+ android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel =
+ new android.hardware.soundtrigger.V2_0.ConfidenceLevel();
+ halLevel.userId = 31;
+ halLevel.levelPercent = 43;
+ halExtra.levels.add(halLevel);
+ halEvent.phraseExtras.add(halExtra);
+ return halEvent;
+ }
+
+ static ISoundTriggerHwCallback.PhraseRecognitionEvent createPhraseRecognitionEvent_2_1(
+ int hwHandle, int status) {
+ ISoundTriggerHwCallback.PhraseRecognitionEvent halEvent =
+ new ISoundTriggerHwCallback.PhraseRecognitionEvent();
+ halEvent.common = createRecognitionEvent_2_1(hwHandle, status);
+
+ android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halExtra =
+ new android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra();
+ halExtra.id = 123;
+ halExtra.confidenceLevel = 52;
+ halExtra.recognitionModes = android.hardware.soundtrigger.V2_0.RecognitionMode.VOICE_TRIGGER
+ | android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER;
+ android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel =
+ new android.hardware.soundtrigger.V2_0.ConfidenceLevel();
+ halLevel.userId = 31;
+ halLevel.levelPercent = 43;
+ halExtra.levels.add(halLevel);
+ halEvent.phraseExtras.add(halExtra);
+ return halEvent;
+ }
+
+ static void validatePhraseRecognitionEvent(PhraseRecognitionEvent event,
+ @RecognitionStatus int status) {
+ validateRecognitionEvent(event.common, status);
+
+ assertEquals(1, event.phraseExtras.length);
+ assertEquals(123, event.phraseExtras[0].id);
+ assertEquals(52, event.phraseExtras[0].confidenceLevel);
+ assertEquals(RecognitionMode.VOICE_TRIGGER | RecognitionMode.GENERIC_TRIGGER,
+ event.phraseExtras[0].recognitionModes);
+ assertEquals(1, event.phraseExtras[0].levels.length);
+ assertEquals(31, event.phraseExtras[0].levels[0].userId);
+ assertEquals(43, event.phraseExtras[0].levels[0].levelPercent);
+ }
+
+ private static FileDescriptor byteArrayToFileDescriptor(byte[] data) {
+ try {
+ SharedMemory shmem = SharedMemory.create("", data.length);
+ ByteBuffer buffer = shmem.mapReadWrite();
+ buffer.put(data);
+ return shmem.getFileDescriptor();
+ } catch (ErrnoException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/tare/LedgerTest.java b/services/tests/servicestests/src/com/android/server/tare/LedgerTest.java
new file mode 100644
index 0000000..f7e1c60
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/tare/LedgerTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.tare;
+
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+
+import static org.junit.Assert.assertEquals;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Test that the ledger records transactions correctly. */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class LedgerTest {
+
+ @Test
+ public void testInitialState() {
+ final Ledger ledger = new Ledger();
+ assertEquals(0, ledger.getCurrentBalance());
+ assertEquals(0, ledger.get24HourSum("anything", 0));
+ }
+
+ @Test
+ public void testMultipleTransactions() {
+ final Ledger ledger = new Ledger();
+ ledger.recordTransaction(new Ledger.Transaction(0, 1000, "test", null, 5));
+ assertEquals(5, ledger.getCurrentBalance());
+ assertEquals(5, ledger.get24HourSum("test", 60_000));
+ ledger.recordTransaction(new Ledger.Transaction(2000, 2000, "test", null, 25));
+ assertEquals(30, ledger.getCurrentBalance());
+ assertEquals(30, ledger.get24HourSum("test", 60_000));
+ ledger.recordTransaction(new Ledger.Transaction(5000, 5500, "test", null, -10));
+ assertEquals(20, ledger.getCurrentBalance());
+ assertEquals(20, ledger.get24HourSum("test", 60_000));
+ }
+
+ @Test
+ public void test24HourSum() {
+ final Ledger ledger = new Ledger();
+ ledger.recordTransaction(new Ledger.Transaction(0, 1000, "test", null, 500));
+ assertEquals(500, ledger.get24HourSum("test", 24 * HOUR_IN_MILLIS));
+ ledger.recordTransaction(
+ new Ledger.Transaction(2 * HOUR_IN_MILLIS, 3 * HOUR_IN_MILLIS, "test", null, 2500));
+ assertEquals(3000, ledger.get24HourSum("test", 24 * HOUR_IN_MILLIS));
+ ledger.recordTransaction(
+ new Ledger.Transaction(4 * HOUR_IN_MILLIS, 4 * HOUR_IN_MILLIS, "test", null, 1));
+ assertEquals(3001, ledger.get24HourSum("test", 24 * HOUR_IN_MILLIS));
+ assertEquals(2501, ledger.get24HourSum("test", 25 * HOUR_IN_MILLIS));
+ assertEquals(2501, ledger.get24HourSum("test", 26 * HOUR_IN_MILLIS));
+ // Pro-rated as the second transaction phases out
+ assertEquals(1251,
+ ledger.get24HourSum("test", 26 * HOUR_IN_MILLIS + 30 * MINUTE_IN_MILLIS));
+ assertEquals(1, ledger.get24HourSum("test", 27 * HOUR_IN_MILLIS));
+ assertEquals(0, ledger.get24HourSum("test", 28 * HOUR_IN_MILLIS));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/tare/OWNERS b/services/tests/servicestests/src/com/android/server/tare/OWNERS
new file mode 100644
index 0000000..217a5ed
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/tare/OWNERS
@@ -0,0 +1 @@
+include /apex/jobscheduler/service/java/com/android/server/tare/OWNERS
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
index 8af2c4d..28838ae 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -97,7 +97,7 @@
try {
mTimeZoneDetectorService.getCapabilitiesAndConfig();
- fail();
+ fail("Expected SecurityException");
} finally {
verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
@@ -129,7 +129,7 @@
ITimeZoneDetectorListener mockListener = mock(ITimeZoneDetectorListener.class);
try {
mTimeZoneDetectorService.addListener(mockListener);
- fail();
+ fail("Expected SecurityException");
} finally {
verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
@@ -235,7 +235,7 @@
try {
mTimeZoneDetectorService.suggestGeolocationTimeZone(timeZoneSuggestion);
- fail();
+ fail("Expected SecurityException");
} finally {
verify(mMockContext).enforceCallingOrSelfPermission(
eq(android.Manifest.permission.SET_TIME_ZONE),
@@ -268,7 +268,7 @@
try {
mTimeZoneDetectorService.suggestManualTimeZone(timeZoneSuggestion);
- fail();
+ fail("Expected SecurityException");
} finally {
verify(mMockContext).enforceCallingOrSelfPermission(
eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE),
@@ -301,7 +301,7 @@
try {
mTimeZoneDetectorService.suggestTelephonyTimeZone(timeZoneSuggestion);
- fail();
+ fail("Expected SecurityException");
} finally {
verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE),
@@ -317,7 +317,7 @@
try {
mTimeZoneDetectorService.suggestTelephonyTimeZone(timeZoneSuggestion);
- fail();
+ fail("Expected SecurityException");
} finally {
verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE),
diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
index aadab6e..2f36c7f 100644
--- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
@@ -365,13 +365,13 @@
mTunerResourceManagerService.registerClientProfileInternal(
profiles[0], listener, clientId0);
assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.getClientProfile(clientId0[0])
- .setPriority(clientPriorities[0]);
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ clientId0[0], clientPriorities[0], 0/*niceValue*/);
mTunerResourceManagerService.registerClientProfileInternal(
profiles[1], new TestResourcesReclaimListener(), clientId1);
assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.getClientProfile(clientId1[0])
- .setPriority(clientPriorities[1]);
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ clientId1[0], clientPriorities[1], 0/*niceValue*/);
// Init frontend resources.
TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
@@ -415,13 +415,13 @@
mTunerResourceManagerService.registerClientProfileInternal(
profiles[0], listener, clientId0);
assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.getClientProfile(clientId0[0])
- .setPriority(clientPriorities[0]);
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ clientId0[0], clientPriorities[0], 0/*niceValue*/);
mTunerResourceManagerService.registerClientProfileInternal(
profiles[1], new TestResourcesReclaimListener(), clientId1);
assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.getClientProfile(clientId1[0])
- .setPriority(clientPriorities[1]);
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ clientId1[0], clientPriorities[1], 0/*niceValue*/);
// Init frontend resources.
TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
@@ -511,13 +511,13 @@
mTunerResourceManagerService.registerClientProfileInternal(
profiles[0], listener, clientId0);
assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.getClientProfile(clientId0[0])
- .setPriority(clientPriorities[0]);
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ clientId0[0], clientPriorities[0], 0/*niceValue*/);
mTunerResourceManagerService.registerClientProfileInternal(
profiles[1], new TestResourcesReclaimListener(), clientId1);
assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.getClientProfile(clientId1[0])
- .setPriority(clientPriorities[1]);
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ clientId1[0], clientPriorities[1], 0/*niceValue*/);
// Init cas resources.
mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
@@ -567,13 +567,13 @@
mTunerResourceManagerService.registerClientProfileInternal(
profiles[0], listener, clientId0);
assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.getClientProfile(clientId0[0])
- .setPriority(clientPriorities[0]);
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ clientId0[0], clientPriorities[0], 0/*niceValue*/);
mTunerResourceManagerService.registerClientProfileInternal(
profiles[1], new TestResourcesReclaimListener(), clientId1);
assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.getClientProfile(clientId1[0])
- .setPriority(clientPriorities[1]);
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ clientId1[0], clientPriorities[1], 0/*niceValue*/);
// Init cicam/cas resources.
mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
@@ -697,13 +697,13 @@
mTunerResourceManagerService.registerClientProfileInternal(
profiles[0], listener, clientId0);
assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.getClientProfile(clientId0[0])
- .setPriority(clientPriorities[0]);
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ clientId0[0], clientPriorities[0], 0/*niceValue*/);
mTunerResourceManagerService.registerClientProfileInternal(
profiles[1], new TestResourcesReclaimListener(), clientId1);
assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.getClientProfile(clientId1[0])
- .setPriority(clientPriorities[1]);
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ clientId1[0], clientPriorities[1], 0/*niceValue*/);
// Init lnb resources.
int[] lnbHandles = {1};
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index a246917..b878d9b 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -114,6 +114,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Random;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -166,6 +167,8 @@
/** Mock variable used in {@link MyInjector#isPackageInstalled(String, int, int)} */
private static boolean isPackageInstalled = true;
+ private static final Random sRandom = new Random();
+
private MyInjector mInjector;
private AppStandbyController mController;
@@ -294,7 +297,7 @@
@Override
File getDataSystemDirectory() {
- return new File(getContext().getFilesDir(), Long.toString(Math.randomLongInternal()));
+ return new File(getContext().getFilesDir(), Long.toString(sRandom.nextLong()));
}
@Override
diff --git a/services/tests/servicestests/test-apps/PackageParserApp/Android.bp b/services/tests/servicestests/test-apps/PackageParserApp/Android.bp
index b11c85c0..c611e38 100644
--- a/services/tests/servicestests/test-apps/PackageParserApp/Android.bp
+++ b/services/tests/servicestests/test-apps/PackageParserApp/Android.bp
@@ -74,3 +74,17 @@
resource_dirs: ["res"],
manifest: "AndroidManifestApp4.xml",
}
+
+android_test_helper_app {
+ name: "PackageParserTestApp5",
+ sdk_version: "current",
+ srcs: ["**/*.java"],
+ dex_preopt: {
+ enabled: false,
+ },
+ optimize: {
+ enabled: false,
+ },
+ resource_dirs: ["res"],
+ manifest: "AndroidManifestApp5.xml",
+}
diff --git a/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp5.xml b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp5.xml
new file mode 100644
index 0000000..cc6caad
--- /dev/null
+++ b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp5.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.servicestests.apps.packageparserapp" >
+
+ <uses-sdk android:minSdkVersion="3"
+ android:targetSdkVersion="3" />
+
+ <application>
+ <activity android:name=".TestActivity"
+ android:exported="true" />
+ </application>
+
+</manifest>
\ No newline at end of file
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
index fd68046..0169a7d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
@@ -16,10 +16,13 @@
package com.android.server.notification;
+import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -116,4 +119,15 @@
SmallHash.hash(group.hashCode()),
p.getGroupIdHash());
}
+
+ @Test
+ public void testIsForegroundService() {
+ NotificationRecordLogger.NotificationRecordPair p = getNotificationRecordPair(
+ 0, null);
+ assertFalse(NotificationRecordLogger.isForegroundService(p.r));
+
+ // Set foreground service
+ p.r.getSbn().getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+ assertTrue(NotificationRecordLogger.isForegroundService(p.r));
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 82c459c..1bbb0da 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -259,7 +259,7 @@
notifyActivityLaunching(mTopActivity.intent);
notifyActivityLaunched(START_SUCCESS, mTopActivity);
doReturn(true).when(mTopActivity.mDisplayContent).isSleeping();
- mTopActivity.setState(Task.ActivityState.RESUMED, "test");
+ mTopActivity.setState(ActivityRecord.State.RESUMED, "test");
mTopActivity.setVisibility(false);
waitHandlerIdle(mAtm.mH);
// Not cancel immediately because in one of real cases, the keyguard may be going away or
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 8216830..b03a66c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -65,19 +65,19 @@
import static com.android.server.wm.ActivityRecord.FINISH_RESULT_CANCELLED;
import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REMOVED;
import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REQUESTED;
-import static com.android.server.wm.Task.ActivityState.DESTROYED;
-import static com.android.server.wm.Task.ActivityState.DESTROYING;
-import static com.android.server.wm.Task.ActivityState.FINISHING;
-import static com.android.server.wm.Task.ActivityState.INITIALIZING;
-import static com.android.server.wm.Task.ActivityState.PAUSED;
-import static com.android.server.wm.Task.ActivityState.PAUSING;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
-import static com.android.server.wm.Task.ActivityState.STARTED;
-import static com.android.server.wm.Task.ActivityState.STOPPED;
-import static com.android.server.wm.Task.ActivityState.STOPPING;
-import static com.android.server.wm.Task.TASK_VISIBILITY_INVISIBLE;
-import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE;
-import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
+import static com.android.server.wm.ActivityRecord.State.DESTROYED;
+import static com.android.server.wm.ActivityRecord.State.DESTROYING;
+import static com.android.server.wm.ActivityRecord.State.FINISHING;
+import static com.android.server.wm.ActivityRecord.State.INITIALIZING;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STARTED;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_AFTER_ANIM;
import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_BEFORE_ANIM;
@@ -133,7 +133,7 @@
import androidx.test.filters.MediumTest;
import com.android.internal.R;
-import com.android.server.wm.Task.ActivityState;
+import com.android.server.wm.ActivityRecord.State;
import org.junit.Before;
import org.junit.Test;
@@ -340,7 +340,7 @@
public void testSetsRelaunchReason_NotDragResizing() {
final ActivityRecord activity = createActivityWithTask();
final Task task = activity.getTask();
- activity.setState(Task.ActivityState.RESUMED, "Testing");
+ activity.setState(RESUMED, "Testing");
task.onRequestedOverrideConfigurationChanged(task.getConfiguration());
activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
@@ -365,7 +365,7 @@
public void testSetsRelaunchReason_DragResizing() {
final ActivityRecord activity = createActivityWithTask();
final Task task = activity.getTask();
- activity.setState(Task.ActivityState.RESUMED, "Testing");
+ activity.setState(RESUMED, "Testing");
task.onRequestedOverrideConfigurationChanged(task.getConfiguration());
activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
@@ -392,7 +392,7 @@
public void testRelaunchClearTopWaitingTranslucent() {
final ActivityRecord activity = createActivityWithTask();
final Task task = activity.getTask();
- activity.setState(Task.ActivityState.RESUMED, "Testing");
+ activity.setState(RESUMED, "Testing");
task.onRequestedOverrideConfigurationChanged(task.getConfiguration());
activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
@@ -413,7 +413,7 @@
public void testSetsRelaunchReason_NonResizeConfigChanges() {
final ActivityRecord activity = createActivityWithTask();
final Task task = activity.getTask();
- activity.setState(Task.ActivityState.RESUMED, "Testing");
+ activity.setState(RESUMED, "Testing");
task.onRequestedOverrideConfigurationChanged(task.getConfiguration());
activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
@@ -461,7 +461,7 @@
.setCreateTask(true)
.setConfigChanges(CONFIG_ORIENTATION | CONFIG_SCREEN_LAYOUT)
.build();
- activity.setState(Task.ActivityState.RESUMED, "Testing");
+ activity.setState(RESUMED, "Testing");
activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
activity.getConfiguration()));
@@ -624,7 +624,7 @@
@Test
public void testShouldMakeActive_deferredResume() {
final ActivityRecord activity = createActivityWithTask();
- activity.setState(Task.ActivityState.STOPPED, "Testing");
+ activity.setState(STOPPED, "Testing");
mSupervisor.beginDeferResume();
assertEquals(false, activity.shouldMakeActive(null /* activeActivity */));
@@ -640,7 +640,7 @@
ActivityRecord finishingActivity = new ActivityBuilder(mAtm).setTask(task).build();
finishingActivity.finishing = true;
ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
- activity.setState(Task.ActivityState.STOPPED, "Testing");
+ activity.setState(STOPPED, "Testing");
assertEquals(false, activity.shouldMakeActive(null /* activeActivity */));
}
@@ -649,15 +649,16 @@
public void testShouldResume_stackVisibility() {
final ActivityRecord activity = createActivityWithTask();
final Task task = activity.getTask();
- activity.setState(Task.ActivityState.STOPPED, "Testing");
+ activity.setState(STOPPED, "Testing");
- doReturn(TASK_VISIBILITY_VISIBLE).when(task).getVisibility(null);
+ doReturn(TASK_FRAGMENT_VISIBILITY_VISIBLE).when(task).getVisibility(null);
assertEquals(true, activity.shouldResumeActivity(null /* activeActivity */));
- doReturn(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT).when(task).getVisibility(null);
+ doReturn(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT)
+ .when(task).getVisibility(null);
assertEquals(false, activity.shouldResumeActivity(null /* activeActivity */));
- doReturn(TASK_VISIBILITY_INVISIBLE).when(task).getVisibility(null);
+ doReturn(TASK_FRAGMENT_VISIBILITY_INVISIBLE).when(task).getVisibility(null);
assertEquals(false, activity.shouldResumeActivity(null /* activeActivity */));
}
@@ -665,13 +666,13 @@
public void testShouldResumeOrPauseWithResults() {
final ActivityRecord activity = createActivityWithTask();
final Task task = activity.getTask();
- activity.setState(Task.ActivityState.STOPPED, "Testing");
+ activity.setState(STOPPED, "Testing");
ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
activity.addResultLocked(topActivity, "resultWho", 0, 0, new Intent());
topActivity.finishing = true;
- doReturn(TASK_VISIBILITY_VISIBLE).when(task).getVisibility(null);
+ doReturn(TASK_FRAGMENT_VISIBILITY_VISIBLE).when(task).getVisibility(null);
assertEquals(true, activity.shouldResumeActivity(null /* activeActivity */));
assertEquals(false, activity.shouldPauseActivity(null /*activeActivity */));
}
@@ -684,7 +685,7 @@
.setConfigChanges(CONFIG_ORIENTATION | CONFIG_SCREEN_LAYOUT)
.build();
final Task task = activity.getTask();
- activity.setState(Task.ActivityState.STOPPED, "Testing");
+ activity.setState(STOPPED, "Testing");
final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
try {
@@ -727,7 +728,7 @@
final ActivityRecord activity = createActivityWithTask();
ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(activity.getTask()).build();
topActivity.setOccludesParent(false);
- activity.setState(Task.ActivityState.STOPPED, "Testing");
+ activity.setState(STOPPED, "Testing");
activity.setVisibility(true);
activity.makeActiveIfNeeded(null /* activeActivity */);
assertEquals(STARTED, activity.getState());
@@ -1011,8 +1012,8 @@
@Test
public void testFinishActivityIfPossible_nonResumedFinishCompletesImmediately() {
final ActivityRecord activity = createActivityWithTask();
- final ActivityState[] states = {INITIALIZING, STARTED, PAUSED, STOPPING, STOPPED};
- for (ActivityState state : states) {
+ final State[] states = {INITIALIZING, STARTED, PAUSED, STOPPING, STOPPED};
+ for (State state : states) {
activity.finishing = false;
activity.setState(state, "test");
reset(activity);
@@ -1393,7 +1394,7 @@
}
private void testCompleteFinishing_ensureActivitiesVisible(boolean diffTask,
- ActivityState secondActivityState) {
+ State secondActivityState) {
final ActivityRecord activity = createActivityWithTask();
final Task task = activity.getTask();
final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(task).build();
@@ -1903,8 +1904,7 @@
assertTrue(wpc.registeredForActivityConfigChanges());
// Create a new task with custom config to reparent the activity to.
- final Task newTask =
- new TaskBuilder(mSupervisor).setParentTask(initialTask.getRootTask()).build();
+ final Task newTask = new TaskBuilder(mSupervisor).build();
final Configuration newConfig = newTask.getConfiguration();
newConfig.densityDpi += 100;
newTask.onRequestedOverrideConfigurationChanged(newConfig);
@@ -1936,8 +1936,7 @@
.diff(wpc.getRequestedOverrideConfiguration()));
// Create a new task with custom config to reparent the second activity to.
- final Task newTask =
- new TaskBuilder(mSupervisor).setParentTask(initialTask.getRootTask()).build();
+ final Task newTask = new TaskBuilder(mSupervisor).build();
final Configuration newConfig = newTask.getConfiguration();
newConfig.densityDpi += 100;
newTask.onRequestedOverrideConfigurationChanged(newConfig);
@@ -2147,7 +2146,7 @@
assertFalse(activity.supportsPictureInPicture());
}
- private void verifyProcessInfoUpdate(ActivityRecord activity, ActivityState state,
+ private void verifyProcessInfoUpdate(ActivityRecord activity, State state,
boolean shouldUpdate, boolean activityChange) {
reset(activity.app);
activity.setState(state, "test");
@@ -2699,6 +2698,40 @@
assertFalse("Starting window should not be present", activity.hasStartingWindow());
}
+ @Test
+ public void testCloseToSquareFixedOrientationPortrait() {
+ // create a square display
+ final DisplayContent squareDisplay = new TestDisplayContent.Builder(mAtm, 2000, 2000)
+ .setSystemDecorations(true).build();
+ final Task task = new TaskBuilder(mSupervisor).setDisplay(squareDisplay).build();
+
+ // create a fixed portrait activity
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task)
+ .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT).build();
+
+ // check that both the configuration and app bounds are portrait
+ assertEquals(ORIENTATION_PORTRAIT, activity.getConfiguration().orientation);
+ assertTrue(activity.getConfiguration().windowConfiguration.getAppBounds().width()
+ <= activity.getConfiguration().windowConfiguration.getAppBounds().height());
+ }
+
+ @Test
+ public void testCloseToSquareFixedOrientationLandscape() {
+ // create a square display
+ final DisplayContent squareDisplay = new TestDisplayContent.Builder(mAtm, 2000, 2000)
+ .setSystemDecorations(true).build();
+ final Task task = new TaskBuilder(mSupervisor).setDisplay(squareDisplay).build();
+
+ // create a fixed landscape activity
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task)
+ .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE).build();
+
+ // check that both the configuration and app bounds are landscape
+ assertEquals(ORIENTATION_LANDSCAPE, activity.getConfiguration().orientation);
+ assertTrue(activity.getConfiguration().windowConfiguration.getAppBounds().width()
+ > activity.getConfiguration().windowConfiguration.getAppBounds().height());
+ }
+
private void assertHasStartingWindow(ActivityRecord atoken) {
assertNotNull(atoken.mStartingSurface);
assertNotNull(atoken.mStartingData);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 2558259..b7726c1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -24,6 +24,10 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -242,7 +246,7 @@
.setTask(mRootWindowContainer.getDefaultTaskDisplayArea().getOrCreateRootHomeTask())
.build();
final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
- activity.setState(Task.ActivityState.RESUMED, "test");
+ activity.setState(RESUMED, "test");
mSupervisor.endDeferResume();
assertEquals(activity.app, mAtm.mInternal.getTopApp());
@@ -252,13 +256,13 @@
activity.mVisibleRequested = false;
activity.setVisible(false);
activity.getTask().setPausingActivity(activity);
- homeActivity.setState(Task.ActivityState.PAUSED, "test");
+ homeActivity.setState(PAUSED, "test");
// Even the visibility states are invisible, the next activity should be resumed because
// the crashed activity was pausing.
mAtm.mInternal.handleAppDied(activity.app, false /* restarting */,
null /* finishInstrumentationCallback */);
- assertEquals(Task.ActivityState.RESUMED, homeActivity.getState());
+ assertEquals(RESUMED, homeActivity.getState());
assertEquals(homeActivity.app, mAtm.mInternal.getTopApp());
}
@@ -269,7 +273,7 @@
final Task rootHomeTask = mWm.mRoot.getDefaultTaskDisplayArea().getOrCreateRootHomeTask();
final ActivityRecord homeActivity = new ActivityBuilder(mAtm).setTask(rootHomeTask).build();
final ActivityRecord topActivity = new ActivityBuilder(mAtm).setCreateTask(true).build();
- topActivity.setState(Task.ActivityState.RESUMED, "test");
+ topActivity.setState(RESUMED, "test");
final Consumer<ActivityRecord> assertTopNonSleeping = activity -> {
assertFalse(mAtm.mInternal.isSleeping());
@@ -285,7 +289,7 @@
verify(mSupervisor.mGoingToSleepWakeLock).acquire();
doReturn(true).when(mSupervisor.mGoingToSleepWakeLock).isHeld();
- assertEquals(Task.ActivityState.PAUSING, topActivity.getState());
+ assertEquals(PAUSING, topActivity.getState());
assertTrue(mAtm.mInternal.isSleeping());
assertEquals(ActivityManager.PROCESS_STATE_TOP_SLEEPING,
mAtm.mInternal.getTopProcessState());
@@ -296,7 +300,7 @@
final Task topRootTask = topActivity.getRootTask();
doReturn(true).when(rootHomeTask).goToSleepIfPossible(anyBoolean());
doReturn(true).when(topRootTask).goToSleepIfPossible(anyBoolean());
- topActivity.setState(Task.ActivityState.STOPPING, "test");
+ topActivity.setState(STOPPING, "test");
topActivity.activityStopped(null /* newIcicle */, null /* newPersistentState */,
null /* description */);
verify(mSupervisor.mGoingToSleepWakeLock).release();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
index 31d4612..af21e02 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
@@ -35,10 +35,10 @@
import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_LAST;
import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION;
+import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID;
import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS;
import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature;
-import static com.android.server.wm.DisplayAreaPolicyBuilder.KEY_ROOT_DISPLAY_AREA_ID;
import static com.google.common.truth.Truth.assertThat;
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
index d5628fc..c4faaa3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -61,6 +61,7 @@
import android.view.SurfaceControl;
import android.view.View;
import android.view.WindowManager;
+import android.window.DisplayAreaInfo;
import android.window.IDisplayAreaOrganizer;
import com.google.android.collect.Lists;
@@ -566,6 +567,31 @@
.onDisplayAreaVanished(mockDisplayAreaOrganizer, displayArea);
}
+ @Test
+ public void testGetDisplayAreaInfo() {
+ final DisplayArea<WindowContainer> displayArea = new DisplayArea<>(
+ mWm, BELOW_TASKS, "NewArea", FEATURE_VENDOR_FIRST);
+ mDisplayContent.addChild(displayArea, 0);
+ final DisplayAreaInfo info = displayArea.getDisplayAreaInfo();
+
+ assertThat(info.token).isEqualTo(displayArea.mRemoteToken.toWindowContainerToken());
+ assertThat(info.configuration).isEqualTo(displayArea.getConfiguration());
+ assertThat(info.displayId).isEqualTo(mDisplayContent.getDisplayId());
+ assertThat(info.featureId).isEqualTo(displayArea.mFeatureId);
+ assertThat(info.rootDisplayAreaId).isEqualTo(mDisplayContent.mFeatureId);
+
+ final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea();
+ final int tdaIndex = tda.getParent().mChildren.indexOf(tda);
+ final RootDisplayArea root =
+ new DisplayAreaGroup(mWm, "TestRoot", FEATURE_VENDOR_FIRST + 1);
+ mDisplayContent.addChild(root, tdaIndex + 1);
+ displayArea.reparent(root, 0);
+
+ final DisplayAreaInfo info2 = displayArea.getDisplayAreaInfo();
+
+ assertThat(info2.rootDisplayAreaId).isEqualTo(root.mFeatureId);
+ }
+
private static class TestDisplayArea<T extends WindowContainer> extends DisplayArea<T> {
private TestDisplayArea(WindowManagerService wms, Rect bounds) {
super(wms, ANY, "half display area");
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 5bc4c82..baa8645 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1533,7 +1533,7 @@
unblockDisplayRotation(mDisplayContent);
final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build();
app.setVisible(false);
- app.setState(Task.ActivityState.RESUMED, "test");
+ app.setState(ActivityRecord.State.RESUMED, "test");
mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_OPEN);
mDisplayContent.mOpeningApps.add(app);
final int newOrientation = getRotatedOrientation(mDisplayContent);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index d017c19..1b19a28 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -31,8 +31,8 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
-import static com.android.server.wm.Task.ActivityState.PAUSED;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.google.common.truth.Truth.assertThat;
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
index 0c6545c..7ebf775 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
@@ -36,22 +36,23 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.ActivityRecord.State.DESTROYED;
+import static com.android.server.wm.ActivityRecord.State.DESTROYING;
+import static com.android.server.wm.ActivityRecord.State.FINISHING;
+import static com.android.server.wm.ActivityRecord.State.INITIALIZING;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
-import static com.android.server.wm.Task.ActivityState.DESTROYED;
-import static com.android.server.wm.Task.ActivityState.DESTROYING;
-import static com.android.server.wm.Task.ActivityState.FINISHING;
-import static com.android.server.wm.Task.ActivityState.PAUSING;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
-import static com.android.server.wm.Task.ActivityState.STOPPED;
-import static com.android.server.wm.Task.ActivityState.STOPPING;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
import static com.android.server.wm.Task.REPARENT_KEEP_ROOT_TASK_AT_FRONT;
import static com.android.server.wm.Task.REPARENT_MOVE_ROOT_TASK_TO_FRONT;
-import static com.android.server.wm.Task.TASK_VISIBILITY_INVISIBLE;
-import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE;
-import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
import static com.android.server.wm.TaskDisplayArea.getRootTaskAbove;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
@@ -280,11 +281,11 @@
public void testResumedActivity() {
final ActivityRecord r = new ActivityBuilder(mAtm).setCreateTask(true).build();
final Task task = r.getTask();
- assertNull(task.getResumedActivity());
+ assertNull(task.getTopResumedActivity());
r.setState(RESUMED, "testResumedActivity");
- assertEquals(r, task.getResumedActivity());
+ assertEquals(r, task.getTopResumedActivity());
r.setState(PAUSING, "testResumedActivity");
- assertNull(task.getResumedActivity());
+ assertNull(task.getTopResumedActivity());
}
@Test
@@ -295,15 +296,15 @@
final Task task = r.getTask();
// Ensure moving task between two root tasks updates resumed activity
r.setState(RESUMED, "testResumedActivityFromTaskReparenting");
- assertEquals(r, rootTask.getResumedActivity());
+ assertEquals(r, rootTask.getTopResumedActivity());
final Task destRootTask = new TaskBuilder(mSupervisor).setOnTop(true).build();
task.reparent(destRootTask, true /* toTop */, REPARENT_KEEP_ROOT_TASK_AT_FRONT,
false /* animate */, true /* deferResume*/,
"testResumedActivityFromTaskReparenting");
- assertNull(rootTask.getResumedActivity());
- assertEquals(r, destRootTask.getResumedActivity());
+ assertNull(rootTask.getTopResumedActivity());
+ assertEquals(r, destRootTask.getTopResumedActivity());
}
@Test
@@ -314,15 +315,15 @@
final Task task = r.getTask();
// Ensure moving task between two root tasks updates resumed activity
r.setState(RESUMED, "testResumedActivityFromActivityReparenting");
- assertEquals(r, rootTask.getResumedActivity());
+ assertEquals(r, rootTask.getTopResumedActivity());
final Task destRootTask = new TaskBuilder(mSupervisor).setOnTop(true).build();
task.reparent(destRootTask, true /*toTop*/, REPARENT_MOVE_ROOT_TASK_TO_FRONT,
false /* animate */, false /* deferResume*/,
"testResumedActivityFromActivityReparenting");
- assertNull(rootTask.getResumedActivity());
- assertEquals(r, destRootTask.getResumedActivity());
+ assertNull(rootTask.getTopResumedActivity());
+ assertEquals(r, destRootTask.getTopResumedActivity());
}
@Test
@@ -618,9 +619,9 @@
doReturn(false).when(splitScreenSecondary2).isTranslucent(any());
assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
splitScreenSecondary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
splitScreenSecondary2.getVisibility(null /* starting */));
// First split-screen secondary should be visible behind another translucent split-screen
@@ -628,9 +629,9 @@
doReturn(true).when(splitScreenSecondary2).isTranslucent(any());
assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
splitScreenSecondary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
splitScreenSecondary2.getVisibility(null /* starting */));
final Task assistantRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
@@ -642,13 +643,13 @@
assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
assertFalse(splitScreenSecondary2.shouldBeVisible(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
assistantRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
splitScreenPrimary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
splitScreenSecondary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
splitScreenSecondary2.getVisibility(null /* starting */));
// Split-screen root tasks should be visible behind a translucent fullscreen root task.
@@ -657,13 +658,13 @@
assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
assistantRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
splitScreenPrimary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
splitScreenSecondary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
splitScreenSecondary2.getVisibility(null /* starting */));
// Assistant root task shouldn't be visible behind translucent split-screen root task,
@@ -678,25 +679,25 @@
assertTrue(assistantRootTask.shouldBeVisible(null /* starting */));
assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertFalse(splitScreenSecondary2.shouldBeVisible(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
assistantRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
splitScreenPrimary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
splitScreenSecondary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
splitScreenSecondary2.getVisibility(null /* starting */));
} else {
assertFalse(assistantRootTask.shouldBeVisible(null /* starting */));
assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
assistantRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
splitScreenPrimary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
splitScreenSecondary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
splitScreenSecondary2.getVisibility(null /* starting */));
}
}
@@ -707,45 +708,51 @@
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, true /* onTop */);
final Task splitPrimary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED, true /* onTop */);
+ // Creating as two-level tasks so home task can be reparented to split-secondary root task.
final Task splitSecondary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
- WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_UNDEFINED, true /* onTop */);
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_UNDEFINED, true /* onTop */,
+ true /* twoLevelTask */);
doReturn(false).when(homeRootTask).isTranslucent(any());
doReturn(false).when(splitPrimary).isTranslucent(any());
doReturn(false).when(splitSecondary).isTranslucent(any());
-
// Re-parent home to split secondary.
homeRootTask.reparent(splitSecondary, POSITION_TOP);
// Current tasks should be visible.
- assertEquals(TASK_VISIBILITY_VISIBLE, splitPrimary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE, splitSecondary.getVisibility(null /* starting */));
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
+ splitPrimary.getVisibility(null /* starting */));
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
+ splitSecondary.getVisibility(null /* starting */));
// Home task should still be visible even though it is a child of another visible task.
- assertEquals(TASK_VISIBILITY_VISIBLE, homeRootTask.getVisibility(null /* starting */));
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
+ homeRootTask.getVisibility(null /* starting */));
// Add fullscreen translucent task that partially occludes split tasks
final Task translucentRootTask = createStandardRootTaskForVisibilityTest(
WINDOWING_MODE_FULLSCREEN, true /* translucent */);
// Fullscreen translucent task should be visible
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
translucentRootTask.getVisibility(null /* starting */));
// Split tasks should be visible behind translucent
- assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
splitPrimary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
splitSecondary.getVisibility(null /* starting */));
// Home task should be visible behind translucent since its parent is visible behind
// translucent.
- assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
homeRootTask.getVisibility(null /* starting */));
// Hide split-secondary
splitSecondary.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, true /* set */);
// Home split secondary and home task should be invisible.
- assertEquals(TASK_VISIBILITY_INVISIBLE, splitSecondary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE, homeRootTask.getVisibility(null /* starting */));
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
+ splitSecondary.getVisibility(null /* starting */));
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
+ homeRootTask.getVisibility(null /* starting */));
}
@Test
@@ -757,9 +764,9 @@
createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
- assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
bottomRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
translucentRootTask.getVisibility(null /* starting */));
}
@@ -775,10 +782,12 @@
createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
- assertEquals(TASK_VISIBILITY_INVISIBLE, bottomRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
+ bottomRootTask.getVisibility(null /* starting */));
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
translucentRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE, opaqueRootTask.getVisibility(null /* starting */));
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
+ opaqueRootTask.getVisibility(null /* starting */));
}
@Test
@@ -793,10 +802,11 @@
createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
- assertEquals(TASK_VISIBILITY_INVISIBLE, bottomRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
+ bottomRootTask.getVisibility(null /* starting */));
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
opaqueRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
translucentRootTask.getVisibility(null /* starting */));
}
@@ -809,9 +819,9 @@
createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
- assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
bottomTranslucentRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
translucentRootTask.getVisibility(null /* starting */));
}
@@ -824,9 +834,10 @@
createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
- assertEquals(TASK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
bottomTranslucentRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE, opaqueRootTask.getVisibility(null /* starting */));
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
+ opaqueRootTask.getVisibility(null /* starting */));
}
@Test
@@ -840,16 +851,17 @@
final Task pinnedRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
bottomRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
translucentRootTask.getVisibility(null /* starting */));
// Add an activity to the pinned root task so it isn't considered empty for visibility
// check.
final ActivityRecord pinnedActivity = new ActivityBuilder(mAtm)
.setTask(pinnedRootTask)
.build();
- assertEquals(TASK_VISIBILITY_VISIBLE, pinnedRootTask.getVisibility(null /* starting */));
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
+ pinnedRootTask.getVisibility(null /* starting */));
}
@Test
@@ -1142,12 +1154,12 @@
} else if (twoLevelTask) {
task = new TaskBuilder(mSupervisor)
.setTaskDisplayArea(taskDisplayArea)
- .setWindowingMode(windowingMode)
.setActivityType(activityType)
.setOnTop(onTop)
.setCreateActivity(true)
.setCreateParentTask(true)
.build().getRootTask();
+ task.setWindowingMode(windowingMode);
} else {
task = new TaskBuilder(mSupervisor)
.setTaskDisplayArea(taskDisplayArea)
@@ -1301,9 +1313,9 @@
final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
topActivity.info.flags |= FLAG_RESUME_WHILE_PAUSING;
- task.startPausingLocked(false /* uiSleeping */, topActivity,
+ task.startPausing(false /* uiSleeping */, topActivity,
"test");
- verify(task).completePauseLocked(anyBoolean(), eq(topActivity));
+ verify(task).completePause(anyBoolean(), eq(topActivity));
}
@Test
@@ -1544,7 +1556,7 @@
activities[i] = r;
doReturn(null).when(mAtm).getProcessController(
eq(r.processName), eq(r.info.applicationInfo.uid));
- r.setState(Task.ActivityState.INITIALIZING, "test");
+ r.setState(INITIALIZING, "test");
// Ensure precondition that the activity is opaque.
assertTrue(r.occludesParent());
mSupervisor.startSpecificActivity(r, false /* andResume */,
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 9267285..c26bc3d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -30,13 +30,14 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.wm.ActivityRecord.State.FINISHING;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE;
-import static com.android.server.wm.Task.ActivityState.FINISHING;
-import static com.android.server.wm.Task.ActivityState.PAUSED;
-import static com.android.server.wm.Task.ActivityState.PAUSING;
-import static com.android.server.wm.Task.ActivityState.STOPPED;
-import static com.android.server.wm.Task.ActivityState.STOPPING;
import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
import static com.google.common.truth.Truth.assertThat;
@@ -363,7 +364,7 @@
TaskDisplayArea defaultTaskDisplayArea = display.getDefaultTaskDisplayArea();
doReturn(isFocusedTask ? task : null).when(defaultTaskDisplayArea).getFocusedRootTask();
mRootWindowContainer.applySleepTokens(true);
- verify(task, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked();
+ verify(task, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleeping();
verify(task, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked(
null /* target */, null /* targetOptions */);
}
@@ -384,7 +385,7 @@
// landscape and the portrait lockscreen is shown.
activity.setLastReportedConfiguration(
new MergedConfiguration(mAtm.getGlobalConfiguration(), rotatedConfig));
- activity.setState(Task.ActivityState.STOPPED, "sleep");
+ activity.setState(STOPPED, "sleep");
display.setIsSleeping(true);
doReturn(false).when(display).shouldSleep();
@@ -624,7 +625,7 @@
ACTIVITY_TYPE_STANDARD, false /* onTop */));
final ActivityRecord activity = new ActivityBuilder(mAtm)
.setTask(rootTask).setOnTop(true).build();
- activity.setState(Task.ActivityState.RESUMED, "test");
+ activity.setState(RESUMED, "test");
// Assume the task is at the topmost position
assertTrue(rootTask.isTopRootTaskInDisplayArea());
@@ -644,7 +645,7 @@
ACTIVITY_TYPE_STANDARD, false /* onTop */));
final ActivityRecord activity = new ActivityBuilder(mAtm)
.setTask(rootTask).setOnTop(true).build();
- activity.setState(Task.ActivityState.RESUMED, "test");
+ activity.setState(RESUMED, "test");
taskDisplayArea.positionChildAt(POSITION_BOTTOM, rootTask, false /*includingParents*/);
// Assume the task is at the topmost position
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 d2270b5..b47e6c1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -40,8 +40,10 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.ActivityRecord.State.RESTARTING_PROCESS;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
-import static com.android.server.wm.Task.ActivityState.STOPPED;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.google.common.truth.Truth.assertThat;
@@ -129,7 +131,7 @@
doNothing().when(mSupervisor).scheduleRestartTimeout(mActivity);
mActivity.mVisibleRequested = true;
mActivity.setSavedState(null /* savedState */);
- mActivity.setState(Task.ActivityState.RESUMED, "testRestart");
+ mActivity.setState(RESUMED, "testRestart");
prepareUnresizable(mActivity, 1.5f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
final Rect originalOverrideBounds = new Rect(mActivity.getBounds());
@@ -137,7 +139,7 @@
// The visible activity should recompute configuration according to the last parent bounds.
mAtm.mActivityClientController.restartActivityProcessIfVisible(mActivity.appToken);
- assertEquals(Task.ActivityState.RESTARTING_PROCESS, mActivity.getState());
+ assertEquals(RESTARTING_PROCESS, mActivity.getState());
assertNotEquals(originalOverrideBounds, mActivity.getBounds());
}
@@ -401,6 +403,7 @@
assertFitted();
final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
+ final Rect currentAppBounds = mActivity.getWindowConfiguration().getAppBounds();
final Rect originalBounds = new Rect(mActivity.getWindowConfiguration().getBounds());
final int notchHeight = 100;
@@ -428,8 +431,8 @@
// Because the display cannot rotate, the portrait activity will fit the short side of
// display with keeping portrait bounds [200, 0 - 700, 1000] in center.
assertEquals(newDisplayBounds.height(), currentBounds.height());
- assertEquals(currentBounds.height() * newDisplayBounds.height() / newDisplayBounds.width(),
- currentBounds.width());
+ assertEquals(currentAppBounds.height() * newDisplayBounds.height()
+ / newDisplayBounds.width(), currentAppBounds.width());
assertFitted();
// The appBounds should be [200, 100 - 700, 1000].
final Rect appBounds = mActivity.getWindowConfiguration().getAppBounds();
@@ -583,7 +586,7 @@
public void testHandleActivitySizeCompatModeChanged() {
setUpDisplaySizeWithApp(1000, 2000);
doReturn(true).when(mTask).isOrganized();
- mActivity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatModeChanged");
+ mActivity.setState(RESUMED, "testHandleActivitySizeCompatModeChanged");
prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
assertFitted();
@@ -603,7 +606,7 @@
mActivity.mVisibleRequested = true;
mActivity.restartProcessIfVisible();
// The full lifecycle isn't hooked up so manually set state to resumed
- mActivity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatModeChanged");
+ mActivity.setState(RESUMED, "testHandleActivitySizeCompatModeChanged");
mTask.mDisplayContent.handleActivitySizeCompatModeIfNeeded(mActivity);
// Expect null token when switching to non-size-compat mode activity.
@@ -618,7 +621,7 @@
public void testHandleActivitySizeCompatModeChangedOnDifferentTask() {
setUpDisplaySizeWithApp(1000, 2000);
doReturn(true).when(mTask).isOrganized();
- mActivity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatModeChanged");
+ mActivity.setState(RESUMED, "testHandleActivitySizeCompatModeChanged");
prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
assertFitted();
@@ -638,7 +641,7 @@
.setCreateActivity(true).build();
final ActivityRecord secondActivity = secondTask.getTopNonFinishingActivity();
doReturn(true).when(secondTask).isOrganized();
- secondActivity.setState(Task.ActivityState.RESUMED,
+ secondActivity.setState(RESUMED,
"testHandleActivitySizeCompatModeChanged");
prepareUnresizable(secondActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
index b89539c..e32b2aa 100644
--- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
+++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
@@ -178,6 +178,11 @@
}
@Override
+ public SurfaceControl.Transaction setDisplayFlags(IBinder displayToken, int flags) {
+ return this;
+ }
+
+ @Override
public SurfaceControl.Transaction setDisplayProjection(IBinder displayToken,
int orientation, Rect layerStackRect, Rect displayRect) {
return this;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index 67b273a..c45c18d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -37,8 +37,8 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.google.common.truth.Truth.assertThat;
@@ -84,8 +84,7 @@
mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
adjacentRootTask.mCreatedByOrganizer = true;
final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea();
- adjacentRootTask.mAdjacentTask = rootTask;
- rootTask.mAdjacentTask = adjacentRootTask;
+ adjacentRootTask.setAdjacentTaskFragment(rootTask);
taskDisplayArea.setLaunchAdjacentFlagRootTask(adjacentRootTask);
Task actualRootTask = taskDisplayArea.getLaunchRootTask(
@@ -111,8 +110,7 @@
final Task adjacentRootTask = createTask(
mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
adjacentRootTask.mCreatedByOrganizer = true;
- adjacentRootTask.mAdjacentTask = rootTask;
- rootTask.mAdjacentTask = adjacentRootTask;
+ adjacentRootTask.setAdjacentTaskFragment(rootTask);
taskDisplayArea.setLaunchRootTask(rootTask,
new int[]{WINDOWING_MODE_MULTI_WINDOW}, new int[]{ACTIVITY_TYPE_STANDARD});
@@ -133,8 +131,7 @@
mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
adjacentRootTask.mCreatedByOrganizer = true;
final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea();
- adjacentRootTask.mAdjacentTask = rootTask;
- rootTask.mAdjacentTask = adjacentRootTask;
+ adjacentRootTask.setAdjacentTaskFragment(rootTask);
taskDisplayArea.setLaunchAdjacentFlagRootTask(adjacentRootTask);
final Task actualRootTask = taskDisplayArea.getLaunchRootTask(
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index d93ebb3..2b85232 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -1254,7 +1254,8 @@
LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
spyOn(persister);
- final Task task = getTestTask();
+ final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true)
+ .setCreateParentTask(true).build().getRootTask();
task.setHasBeenVisible(false);
task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 3f1248a..8f9b97e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -42,8 +42,8 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.android.server.wm.WindowContainer.SYNC_STATE_READY;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index f848ce5..1240f9b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -23,6 +23,12 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STARTED;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -309,17 +315,17 @@
callbackResult[0] = 0;
activity.mVisibleRequested = false;
- activity.setState(Task.ActivityState.PAUSED, "test");
+ activity.setState(PAUSED, "test");
mWpc.computeOomAdjFromActivities(callback);
assertEquals(paused, callbackResult[0]);
callbackResult[0] = 0;
- activity.setState(Task.ActivityState.STOPPING, "test");
+ activity.setState(STOPPING, "test");
mWpc.computeOomAdjFromActivities(callback);
assertEquals(stopping, callbackResult[0]);
callbackResult[0] = 0;
- activity.setState(Task.ActivityState.STOPPED, "test");
+ activity.setState(STOPPED, "test");
mWpc.computeOomAdjFromActivities(callback);
assertEquals(other, callbackResult[0]);
}
@@ -330,25 +336,25 @@
spyOn(tracker);
final ActivityRecord activity = createActivityRecord(mWpc);
activity.mVisibleRequested = true;
- activity.setState(Task.ActivityState.STARTED, "test");
+ activity.setState(STARTED, "test");
verify(tracker).onAnyActivityVisible(mWpc);
assertTrue(mWpc.hasVisibleActivities());
- activity.setState(Task.ActivityState.RESUMED, "test");
+ activity.setState(RESUMED, "test");
verify(tracker).onActivityResumedWhileVisible(mWpc);
assertTrue(tracker.hasResumedActivity(mWpc.mUid));
activity.makeFinishingLocked();
- activity.setState(Task.ActivityState.PAUSING, "test");
+ activity.setState(PAUSING, "test");
assertFalse(tracker.hasResumedActivity(mWpc.mUid));
assertTrue(mWpc.hasForegroundActivities());
activity.setVisibility(false);
activity.mVisibleRequested = false;
- activity.setState(Task.ActivityState.STOPPED, "test");
+ activity.setState(STOPPED, "test");
verify(tracker).onAllActivitiesInvisible(mWpc);
assertFalse(mWpc.hasVisibleActivities());
diff --git a/services/usage/OWNERS b/services/usage/OWNERS
index 9daa093..6c20e74 100644
--- a/services/usage/OWNERS
+++ b/services/usage/OWNERS
@@ -2,3 +2,5 @@
varunshah@google.com
huiyu@google.com
yamasani@google.com
+
+per-file *StorageStats* = file:/core/java/android/os/storage/OWNERS
\ No newline at end of file
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index b056de0..763159a 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -208,7 +208,7 @@
public boolean isReservedSupported(String volumeUuid, String callingPackage) {
if (volumeUuid == StorageManager.UUID_PRIVATE_INTERNAL) {
return SystemProperties.getBoolean(StorageManager.PROP_HAS_RESERVED, false)
- || Build.IS_CONTAINER;
+ || Build.IS_ARC;
} else {
return false;
}
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 7f24c36..b966643 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -239,8 +239,7 @@
mHandler.updateState(state);
} else if ("GETPROTOCOL".equals(accessory)) {
if (DEBUG) Slog.d(TAG, "got accessory get protocol");
- long elapsedRealtime = SystemClock.elapsedRealtime();
- mHandler.setAccessoryUEventTime(elapsedRealtime);
+ mHandler.setAccessoryUEventTime(SystemClock.elapsedRealtime());
resetAccessoryHandshakeTimeoutHandler();
} else if ("SENDSTRING".equals(accessory)) {
if (DEBUG) Slog.d(TAG, "got accessory send string");
@@ -465,6 +464,8 @@
}
}
+ //TODO It is not clear that this method serves any purpose (at least on Pixel devices)
+ // consider removing
private static void initRndisAddress() {
// configure RNDIS ethernet address based on our serial number using the same algorithm
// we had been previously using in kernel board files
@@ -484,7 +485,7 @@
try {
FileUtils.stringToFile(RNDIS_ETH_ADDR_PATH, addrString);
} catch (IOException e) {
- Slog.e(TAG, "failed to write to " + RNDIS_ETH_ADDR_PATH);
+ Slog.i(TAG, "failed to write to " + RNDIS_ETH_ADDR_PATH);
}
}
@@ -760,9 +761,6 @@
}
private void broadcastUsbAccessoryHandshake() {
- long elapsedRealtime = SystemClock.elapsedRealtime();
- long accessoryHandShakeEnd = elapsedRealtime;
-
// send a sticky broadcast containing USB accessory handshake information
Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_HANDSHAKE)
.putExtra(UsbManager.EXTRA_ACCESSORY_UEVENT_TIME,
@@ -772,7 +770,7 @@
.putExtra(UsbManager.EXTRA_ACCESSORY_START,
mStartAccessory)
.putExtra(UsbManager.EXTRA_ACCESSORY_HANDSHAKE_END,
- accessoryHandShakeEnd);
+ SystemClock.elapsedRealtime());
if (DEBUG) {
Slog.d(TAG, "broadcasting " + intent + " extras: " + intent.getExtras());
@@ -780,7 +778,6 @@
sendStickyBroadcast(intent);
resetUsbAccessoryHandshakeDebuggingInfo();
- accessoryHandShakeEnd = 0L;
}
protected void updateUsbStateBroadcastIfNeeded(long functions) {
@@ -1935,14 +1932,14 @@
}
break;
case MSG_GET_CURRENT_USB_FUNCTIONS:
- Slog.e(TAG, "prcessing MSG_GET_CURRENT_USB_FUNCTIONS");
+ Slog.i(TAG, "processing MSG_GET_CURRENT_USB_FUNCTIONS");
mCurrentUsbFunctionsReceived = true;
if (mCurrentUsbFunctionsRequested) {
- Slog.e(TAG, "updating mCurrentFunctions");
+ Slog.i(TAG, "updating mCurrentFunctions");
// Mask out adb, since it is stored in mAdbEnabled
mCurrentFunctions = ((Long) msg.obj) & ~UsbManager.FUNCTION_ADB;
- Slog.e(TAG,
+ Slog.i(TAG,
"mCurrentFunctions:" + mCurrentFunctions + "applied:" + msg.arg1);
mCurrentFunctionsApplied = msg.arg1 == 1;
}
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index a3b5fc7..be37a91 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -36,7 +36,6 @@
import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent;
import android.hardware.soundtrigger.SoundTrigger.SoundModel;
-import android.hardware.soundtrigger.SoundTrigger.SoundModelEvent;
import android.hardware.soundtrigger.SoundTriggerModule;
import android.os.Binder;
import android.os.DeadObjectException;
@@ -109,9 +108,6 @@
private boolean mCallActive = false;
private @SoundTriggerPowerSaveMode int mSoundTriggerPowerSaveMode =
PowerManager.SOUND_TRIGGER_MODE_ALL_ENABLED;
- // Indicates if the native sound trigger service is disabled or not.
- // This is an indirect indication of the microphone being open in some other application.
- private boolean mServiceDisabled = false;
// Whether ANY recognition (keyphrase or generic) has been requested.
private boolean mRecognitionRequested = false;
@@ -862,23 +858,19 @@
}
@Override
- public void onSoundModelUpdate(SoundModelEvent event) {
- if (event == null) {
- Slog.w(TAG, "Invalid sound model event!");
- return;
- }
- if (DBG) Slog.d(TAG, "onSoundModelUpdate: " + event);
+ public void onModelUnloaded(int modelHandle) {
+ if (DBG) Slog.d(TAG, "onModelUnloaded: " + modelHandle);
synchronized (mLock) {
MetricsLogger.count(mContext, "sth_sound_model_updated", 1);
- onSoundModelUpdatedLocked(event);
+ onModelUnloadedLocked(modelHandle);
}
}
@Override
- public void onServiceStateChange(int state) {
- if (DBG) Slog.d(TAG, "onServiceStateChange, state: " + state);
+ public void onResourcesAvailable() {
+ if (DBG) Slog.d(TAG, "onResourcesAvailable");
synchronized (mLock) {
- onServiceStateChangedLocked(SoundTrigger.SERVICE_STATE_DISABLED == state);
+ onResourcesAvailableLocked();
}
}
@@ -910,15 +902,14 @@
updateAllRecognitionsLocked();
}
- private void onSoundModelUpdatedLocked(SoundModelEvent event) {
- // TODO: Handle sound model update here.
+ private void onModelUnloadedLocked(int modelHandle) {
+ ModelData modelData = getModelDataForLocked(modelHandle);
+ if (modelData != null) {
+ modelData.setNotLoaded();
+ }
}
- private void onServiceStateChangedLocked(boolean disabled) {
- if (disabled == mServiceDisabled) {
- return;
- }
- mServiceDisabled = disabled;
+ private void onResourcesAvailableLocked() {
updateAllRecognitionsLocked();
}
@@ -1039,7 +1030,6 @@
if (mModule != null) {
mModule.detach();
mModule = null;
- mServiceDisabled = false;
}
}
}
@@ -1114,8 +1104,6 @@
pw.print(" call active=");
pw.println(mCallActive);
pw.println(" SoundTrigger Power State=" + mSoundTriggerPowerSaveMode);
- pw.print(" service disabled=");
- pw.println(mServiceDisabled);
}
}
@@ -1329,8 +1317,7 @@
mSoundTriggerPowerSaveMode = mPowerManager.getSoundTriggerPowerSaveMode();
}
- return !mCallActive && !mServiceDisabled
- && isRecognitionAllowedByPowerState(
+ return !mCallActive && isRecognitionAllowedByPowerState(
modelData);
}
@@ -1571,6 +1558,10 @@
mModelState = MODEL_LOADED;
}
+ synchronized void setNotLoaded() {
+ mModelState = MODEL_NOTLOADED;
+ }
+
synchronized boolean isModelStarted() {
return mModelState == MODEL_STARTED;
}
diff --git a/telecomm/TEST_MAPPING b/telecomm/TEST_MAPPING
index 1963ff3..391dce1 100644
--- a/telecomm/TEST_MAPPING
+++ b/telecomm/TEST_MAPPING
@@ -23,6 +23,46 @@
"exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
+ },
+ {
+ "name": "CtsTelephonySdk28TestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsTelephony2TestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsTelephony3TestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsSimRestrictedApisTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsTelephonyProviderTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
}
],
"presubmit-large": [
diff --git a/telephony/TEST_MAPPING b/telephony/TEST_MAPPING
index d585666..02d4eb3 100644
--- a/telephony/TEST_MAPPING
+++ b/telephony/TEST_MAPPING
@@ -23,6 +23,46 @@
"exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
+ },
+ {
+ "name": "CtsTelephony2TestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsTelephonySdk28TestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsTelephony3TestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsSimRestrictedApisTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsTelephonyProviderTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
}
]
}
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 5171cf9..73d1710 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -2666,7 +2666,8 @@
* @throws IllegalArgumentException if contentUri is empty
*/
public void sendMultimediaMessage(@NonNull Context context, @NonNull Uri contentUri,
- @Nullable String locationUrl, @Nullable Bundle configOverrides,
+ @Nullable String locationUrl,
+ @SuppressWarnings("NullableCollection") @Nullable Bundle configOverrides,
@Nullable PendingIntent sentIntent, long messageId) {
if (contentUri == null) {
throw new IllegalArgumentException("Uri contentUri null");
@@ -2742,7 +2743,8 @@
* @throws IllegalArgumentException if locationUrl or contentUri is empty
*/
public void downloadMultimediaMessage(@NonNull Context context, @NonNull String locationUrl,
- @NonNull Uri contentUri, @Nullable Bundle configOverrides,
+ @NonNull Uri contentUri,
+ @SuppressWarnings("NullableCollection") @Nullable Bundle configOverrides,
@Nullable PendingIntent downloadedIntent, long messageId) {
if (TextUtils.isEmpty(locationUrl)) {
throw new IllegalArgumentException("Empty MMS location URL");
diff --git a/telephony/java/android/telephony/TelephonyDisplayInfo.java b/telephony/java/android/telephony/TelephonyDisplayInfo.java
index 88c66ac..f4e2ade 100644
--- a/telephony/java/android/telephony/TelephonyDisplayInfo.java
+++ b/telephony/java/android/telephony/TelephonyDisplayInfo.java
@@ -79,7 +79,7 @@
* <li>The device is connected to the NR cellular network on millimeter wave bands. </li>
* <li>The device is connected to the specific network which the carrier is using
* proprietary means to provide a faster overall data connection than would be otherwise
- * possible. This may include using other bands unique to the carrier, or carrier
+ * possible. This may include using other bands unique to the carrier, or carrier
* aggregation, for example.</li>
* </ul>
* One of the use case is that UX can show a different icon, for example, "5G+"
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 64aa2994..0f32e5d 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -8512,13 +8512,13 @@
/**
* Get the PLMN chosen for Manual Network Selection if active.
- * Return null string if in automatic selection.
+ * Return empty string if in automatic selection.
*
* <p>Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
* READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
* (see {@link #hasCarrierPrivileges})
*
- * @return manually selected network info on success or null string on failure
+ * @return manually selected network info on success or empty string on failure
*/
@SuppressAutoDoc // No support carrier privileges (b/72967236).
@RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
@@ -8531,7 +8531,7 @@
} catch (RemoteException ex) {
Rlog.e(TAG, "getManualNetworkSelectionPlmn RemoteException", ex);
}
- return null;
+ return "";
}
/**
@@ -11115,14 +11115,10 @@
@UnsupportedAppUsage
public int getSubIdForPhoneAccount(@Nullable PhoneAccount phoneAccount) {
int retval = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- try {
- ITelephony service = getITelephony();
- if (service != null) {
- retval = service.getSubIdForPhoneAccount(phoneAccount);
- }
- } catch (RemoteException e) {
+ if (phoneAccount != null
+ && phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
+ retval = getSubscriptionId(phoneAccount.getAccountHandle());
}
-
return retval;
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index a096c1f..da15bea 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1354,11 +1354,6 @@
String callingFeatureId);
/**
- * Returns the subscription ID associated with the specified PhoneAccount.
- */
- int getSubIdForPhoneAccount(in PhoneAccount phoneAccount);
-
- /**
* Returns the subscription ID associated with the specified PhoneAccountHandle.
*/
int getSubIdForPhoneAccountHandle(in PhoneAccountHandle phoneAccountHandle,
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 69e8a8d..25a94e3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -25,7 +25,12 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -73,10 +78,23 @@
}
}
+ @Presubmit
+ @Test
+ fun statusBarWindowIsAlwaysVisible() {
+ testSpec.statusBarWindowIsAlwaysVisible()
+ }
+
+ @FlakyTest
+ @Test
+ fun statusBarLayerIsAlwaysVisible() {
+ testSpec.statusBarLayerIsAlwaysVisible(rotatesScreen = true)
+ }
+
@Postsubmit
@Test
- override fun statusBarLayerRotatesScales() {
- super.statusBarLayerRotatesScales()
+ fun statusBarLayerRotatesScales() {
+ testSpec.statusBarLayerRotatesScales(
+ testSpec.config.startRotation, testSpec.config.endRotation)
}
@Presubmit
@@ -87,8 +105,20 @@
@FlakyTest
@Test
- override fun statusBarLayerIsAlwaysVisible() {
- super.statusBarLayerIsAlwaysVisible()
+ override fun navBarLayerIsAlwaysVisible() {
+ super.navBarLayerIsAlwaysVisible()
+ }
+
+ @FlakyTest
+ @Test
+ override fun navBarLayerRotatesAndScales() {
+ super.navBarLayerRotatesAndScales()
+ }
+
+ @FlakyTest
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
}
companion object {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
index 4b888cd..1739d03 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
@@ -18,7 +18,6 @@
import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
-import androidx.test.filters.FlakyTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerTestParameter
@@ -33,9 +32,6 @@
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.noUncoveredRegions
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.Test
@@ -69,19 +65,19 @@
}
}
- @FlakyTest
+ @Presubmit
@Test
open fun navBarWindowIsAlwaysVisible() {
testSpec.navBarWindowIsAlwaysVisible()
}
- @FlakyTest
+ @Presubmit
@Test
open fun navBarLayerIsAlwaysVisible() {
testSpec.navBarLayerIsAlwaysVisible(rotatesScreen = true)
}
- @FlakyTest
+ @Presubmit
@Test
open fun navBarLayerRotatesAndScales() {
testSpec.navBarLayerRotatesAndScales(
@@ -90,25 +86,6 @@
@Presubmit
@Test
- open fun statusBarWindowIsAlwaysVisible() {
- testSpec.statusBarWindowIsAlwaysVisible()
- }
-
- @FlakyTest
- @Test
- open fun statusBarLayerIsAlwaysVisible() {
- testSpec.statusBarLayerIsAlwaysVisible(rotatesScreen = true)
- }
-
- @FlakyTest
- @Test
- open fun statusBarLayerRotatesScales() {
- testSpec.statusBarLayerRotatesScales(
- testSpec.config.startRotation, testSpec.config.endRotation)
- }
-
- @FlakyTest
- @Test
open fun visibleLayersShownMoreThanOneConsecutiveEntry() {
testSpec.assertLayers {
this.visibleLayersShownMoreThanOneConsecutiveEntry(
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index b153bec..9d788a7 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -18,6 +18,7 @@
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
+import android.view.WindowManager
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -27,6 +28,7 @@
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.SeamlessRotationAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -60,16 +62,33 @@
}
}
- @FlakyTest(bugId = 140855415)
+ @Postsubmit
@Test
- override fun statusBarWindowIsAlwaysVisible() {
- super.statusBarWindowIsAlwaysVisible()
+ fun appWindowFullScreen() {
+ testSpec.assertWm {
+ this.invoke("isFullScreen") {
+ val appWindow = it.windowState(testApp.`package`)
+ val flags = appWindow.windowState?.attributes?.flags ?: 0
+ appWindow.verify("isFullScreen")
+ .that(flags.and(WindowManager.LayoutParams.FLAG_FULLSCREEN))
+ .isGreaterThan(0)
+ }
+ }
}
- @FlakyTest(bugId = 140855415)
+ @Postsubmit
@Test
- override fun statusBarLayerIsAlwaysVisible() {
- super.statusBarLayerIsAlwaysVisible()
+ fun appWindowSeamlessRotation() {
+ testSpec.assertWm {
+ this.invoke("isRotationSeamless") {
+ val appWindow = it.windowState(testApp.`package`)
+ val rotationAnimation = appWindow.windowState?.attributes?.rotationAnimation ?: 0
+ appWindow.verify("isRotationSeamless")
+ .that(rotationAnimation
+ .and(WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS))
+ .isGreaterThan(0)
+ }
+ }
}
@Presubmit
@@ -90,12 +109,46 @@
}
}
+ @Presubmit
+ @Test
+ fun statusBarWindowIsAlwaysInvisible() {
+ testSpec.assertWm {
+ this.hidesAboveAppWindow(WindowManagerStateHelper.STATUS_BAR_WINDOW_NAME)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun statusBarLayerIsAlwaysInvisible() {
+ testSpec.assertLayers {
+ this.isInvisible(WindowManagerStateHelper.STATUS_BAR_LAYER_NAME)
+ }
+ }
+
@Postsubmit
@Test
override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
super.visibleLayersShownMoreThanOneConsecutiveEntry()
}
+ @FlakyTest
+ @Test
+ override fun navBarWindowIsAlwaysVisible() {
+ super.navBarWindowIsAlwaysVisible()
+ }
+
+ @FlakyTest
+ @Test
+ override fun navBarLayerIsAlwaysVisible() {
+ super.navBarLayerIsAlwaysVisible()
+ }
+
+ @FlakyTest
+ @Test
+ override fun navBarLayerRotatesAndScales() {
+ super.navBarLayerRotatesAndScales()
+ }
+
companion object {
private val testFactory = FlickerTestParameterFactory.getInstance()
diff --git a/tests/SoundTriggerTestApp/res/layout/main.xml b/tests/SoundTriggerTestApp/res/layout/main.xml
index 2c6c8d7..1381c0a 100644
--- a/tests/SoundTriggerTestApp/res/layout/main.xml
+++ b/tests/SoundTriggerTestApp/res/layout/main.xml
@@ -73,6 +73,14 @@
android:text="@string/play_trigger"
android:onClick="onPlayTriggerButtonClicked"
android:padding="20dp" />
+
+ <Button
+ android:id="@+id/get_state_id"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/get_model_state"
+ android:onClick="onGetModelStateButtonClicked"
+ android:padding="20dp" />
</LinearLayout>
<LinearLayout
diff --git a/tests/SoundTriggerTestApp/res/values/strings.xml b/tests/SoundTriggerTestApp/res/values/strings.xml
index c48b648..adb0fcf 100644
--- a/tests/SoundTriggerTestApp/res/values/strings.xml
+++ b/tests/SoundTriggerTestApp/res/values/strings.xml
@@ -22,6 +22,7 @@
<string name="start_recog">Start</string>
<string name="stop_recog">Stop</string>
<string name="play_trigger">Play Trigger Audio</string>
+ <string name="get_model_state">Get State</string>
<string name="capture">Capture Audio</string>
<string name="stop_capture">Stop Capturing Audio</string>
<string name="play_capture">Play Captured Audio</string>
diff --git a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestActivity.java b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestActivity.java
index c3c4cf5..72aa38d 100644
--- a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestActivity.java
+++ b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestActivity.java
@@ -257,6 +257,14 @@
}
}
+ public synchronized void onGetModelStateButtonClicked(View v) {
+ if (mService == null) {
+ Log.e(TAG, "Can't get model state: not bound to SoundTriggerTestService");
+ } else {
+ mService.getModelState(mSelectedModelUuid);
+ }
+ }
+
public synchronized void onCaptureAudioCheckboxClicked(View v) {
// See if we have the right permissions
if (!mService.hasMicrophonePermission()) {
diff --git a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java
index 380e299..6d4ffcf 100644
--- a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java
+++ b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java
@@ -23,6 +23,8 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent;
+import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
import android.media.AudioAttributes;
import android.media.AudioFormat;
import android.media.AudioManager;
@@ -46,6 +48,7 @@
import java.util.Random;
import java.util.UUID;
+
public class SoundTriggerTestService extends Service {
private static final String TAG = "SoundTriggerTestSrv";
private static final String INTENT_ACTION = "com.android.intent.action.MANAGE_SOUND_TRIGGER";
@@ -57,6 +60,8 @@
private Random mRandom;
private UserActivity mUserActivity;
+ private static int captureCount;
+
public interface UserActivity {
void addModel(UUID modelUuid, String state);
void setModelState(UUID modelUuid, String state);
@@ -131,6 +136,8 @@
} else if (command.equals("set_capture_timeout")) {
setCaptureAudioTimeout(getModelUuidFromIntent(intent),
intent.getIntExtra("timeout", 5000));
+ } else if (command.equals("get_model_state")) {
+ getModelState(getModelUuidFromIntent(intent));
} else {
Log.e(TAG, "Unknown command '" + command + "'");
}
@@ -432,6 +439,17 @@
return modelInfo != null && modelInfo.captureAudioTrack != null;
}
+ public synchronized void getModelState(UUID modelUuid) {
+ ModelInfo modelInfo = mModelInfoMap.get(modelUuid);
+ if (modelInfo == null) {
+ postError("Could not find model for: " + modelUuid.toString());
+ return;
+ }
+ int status = mSoundTriggerUtil.getModelState(modelUuid);
+ postMessage("GetModelState for: " + modelInfo.name + " returns: "
+ + status);
+ }
+
private void loadModelsInDataDir() {
// Load all the models in the data dir.
boolean loadedModel = false;
@@ -527,18 +545,29 @@
}
}
+
private class CaptureAudioRecorder implements Runnable {
private final ModelInfo mModelInfo;
+
+ // EventPayload and RecognitionEvent are equivalant. Only one will be non-null.
private final SoundTriggerDetector.EventPayload mEvent;
+ private final RecognitionEvent mRecognitionEvent;
public CaptureAudioRecorder(ModelInfo modelInfo, SoundTriggerDetector.EventPayload event) {
mModelInfo = modelInfo;
mEvent = event;
+ mRecognitionEvent = null;
+ }
+
+ public CaptureAudioRecorder(ModelInfo modelInfo, RecognitionEvent event) {
+ mModelInfo = modelInfo;
+ mEvent = null;
+ mRecognitionEvent = event;
}
@Override
public void run() {
- AudioFormat format = mEvent.getCaptureAudioFormat();
+ AudioFormat format = getAudioFormat();
if (format == null) {
postErrorToast("No audio format in recognition event.");
return;
@@ -600,18 +629,21 @@
}
audioRecord = new AudioRecord(attributes, format, bytesRequired,
- mEvent.getCaptureSession());
+ getCaptureSession());
byte[] buffer = new byte[bytesRequired];
// Create a file so we can save the output data there for analysis later.
FileOutputStream fos = null;
try {
- fos = new FileOutputStream( new File(
- getFilesDir() + File.separator
- + mModelInfo.name.replace(' ', '_')
- + "_capture_" + format.getChannelCount() + "ch_"
- + format.getSampleRate() + "hz_" + encoding + ".pcm"));
+ File file = new File(
+ getFilesDir() + File.separator
+ + mModelInfo.name.replace(' ', '_')
+ + "_capture_" + format.getChannelCount() + "ch_"
+ + format.getSampleRate() + "hz_" + encoding
+ + "_" + (++captureCount) + ".pcm");
+ Log.i(TAG, "Writing audio to: " + file);
+ fos = new FileOutputStream(file);
} catch (IOException e) {
Log.e(TAG, "Failed to open output for saving PCM data", e);
postErrorToast("Failed to open output for saving PCM data: "
@@ -635,6 +667,10 @@
bytesRequired -= bytesRead;
}
audioRecord.stop();
+ if (fos != null) {
+ fos.flush();
+ fos.close();
+ }
} catch (Exception e) {
Log.e(TAG, "Error recording trigger audio", e);
postErrorToast("Error recording trigger audio: " + e.getMessage());
@@ -651,6 +687,26 @@
setModelState(mModelInfo, "Recording finished");
}
}
+
+ private AudioFormat getAudioFormat() {
+ if (mEvent != null) {
+ return mEvent.getCaptureAudioFormat();
+ }
+ if (mRecognitionEvent != null) {
+ return mRecognitionEvent.captureFormat;
+ }
+ return null;
+ }
+
+ private int getCaptureSession() {
+ if (mEvent != null) {
+ return mEvent.getCaptureSession();
+ }
+ if (mRecognitionEvent != null) {
+ return mRecognitionEvent.captureSession;
+ }
+ return 0;
+ }
}
// Implementation of SoundTriggerDetector.Callback.
diff --git a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerUtil.java b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerUtil.java
index cfe8c85..996a78f 100644
--- a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerUtil.java
+++ b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerUtil.java
@@ -18,6 +18,8 @@
import android.annotation.Nullable;
import android.content.Context;
+import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent;
+import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
import android.media.soundtrigger.SoundTriggerDetector;
import android.media.soundtrigger.SoundTriggerManager;
import android.os.RemoteException;
@@ -27,6 +29,7 @@
import com.android.internal.app.ISoundTriggerService;
+import java.lang.reflect.Method;
import java.lang.RuntimeException;
import java.util.UUID;
@@ -50,13 +53,31 @@
* The sound model must contain a valid UUID.
*
* @param soundModel The sound model to add/update.
+ * @return The true if the model was loaded successfully, false otherwise.
*/
public boolean addOrUpdateSoundModel(SoundTriggerManager.Model soundModel) {
if (soundModel == null) {
throw new RuntimeException("Bad sound model");
}
mSoundTriggerManager.updateModel(soundModel);
- return true;
+ // TODO: call loadSoundModel in the soundtrigger manager updateModel method
+ // instead of here. It is needed to keep soundtrigger manager internal
+ // state consistent.
+ return mSoundTriggerManager
+ .loadSoundModel(getGenericSoundModel(soundModel)) == 0;
+ }
+
+ private GenericSoundModel getGenericSoundModel(
+ SoundTriggerManager.Model soundModel) {
+ try {
+ Method method = SoundTriggerManager.Model.class
+ .getDeclaredMethod("getGenericSoundModel");
+ method.setAccessible(true);
+ return (GenericSoundModel) method.invoke(soundModel);
+ } catch (ReflectiveOperationException e) {
+ Log.e(TAG, "Failed to getGenericSoundModel: " + soundModel, e);
+ return null;
+ }
}
/**
@@ -92,6 +113,16 @@
return true;
}
+ /**
+ * Get the current model state
+ *
+ * @param modelId The model ID to look-up the sound model for.
+ * @return 0 if the call succeeds, or an error code if it fails.
+ */
+ public int getModelState(UUID modelId) {
+ return mSoundTriggerManager.getModelState(modelId);
+ }
+
public SoundTriggerDetector createSoundTriggerDetector(UUID modelId,
SoundTriggerDetector.Callback callback) {
return mSoundTriggerManager.createSoundTriggerDetector(modelId, callback, null);
diff --git a/tests/UpdatableSystemFontTest/Android.bp b/tests/UpdatableSystemFontTest/Android.bp
index e07fbbf..dc090aa 100644
--- a/tests/UpdatableSystemFontTest/Android.bp
+++ b/tests/UpdatableSystemFontTest/Android.bp
@@ -37,15 +37,19 @@
"vts",
],
data: [
- ":NotoColorEmojiTtf",
+ ":NotoColorEmoji.ttf",
+ ":NotoSerif-Regular.ttf",
+ ":NotoSerif-Bold.ttf",
":UpdatableSystemFontTestCertDer",
- ":UpdatableSystemFontTestNotoColorEmojiTtfFsvSig",
- ":UpdatableSystemFontTestNotoColorEmojiV0Ttf",
- ":UpdatableSystemFontTestNotoColorEmojiV0TtfFsvSig",
- ":UpdatableSystemFontTestNotoColorEmojiVPlus1Ttf",
- ":UpdatableSystemFontTestNotoColorEmojiVPlus1TtfFsvSig",
- ":UpdatableSystemFontTestNotoColorEmojiVPlus2Ttf",
- ":UpdatableSystemFontTestNotoColorEmojiVPlus2TtfFsvSig",
+ ":UpdatableSystemFontTest_NotoColorEmoji.sig",
+ ":UpdatableSystemFontTest_NotoColorEmojiV0.ttf",
+ ":UpdatableSystemFontTest_NotoColorEmojiV0.sig",
+ ":UpdatableSystemFontTest_NotoColorEmojiVPlus1.ttf",
+ ":UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig",
+ ":UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf",
+ ":UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig",
+ ":UpdatableSystemFontTest_NotoSerif-Regular.sig",
+ ":UpdatableSystemFontTest_NotoSerif-Bold.sig",
],
sdk_version: "test_current",
}
diff --git a/tests/UpdatableSystemFontTest/AndroidTest.xml b/tests/UpdatableSystemFontTest/AndroidTest.xml
index 4f6487e..6effa7b 100644
--- a/tests/UpdatableSystemFontTest/AndroidTest.xml
+++ b/tests/UpdatableSystemFontTest/AndroidTest.xml
@@ -29,13 +29,17 @@
<option name="cleanup" value="true" />
<option name="push" value="UpdatableSystemFontTestCert.der->/data/local/tmp/UpdatableSystemFontTestCert.der" />
<option name="push" value="NotoColorEmoji.ttf->/data/local/tmp/NotoColorEmoji.ttf" />
- <option name="push" value="UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig->/data/local/tmp/UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig" />
- <option name="push" value="UpdatableSystemFontTestNotoColorEmojiV0.ttf->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV0.ttf" />
- <option name="push" value="UpdatableSystemFontTestNotoColorEmojiV0.ttf.fsv_sig->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV0.ttf.fsv_sig" />
- <option name="push" value="UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf" />
- <option name="push" value="UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf.fsv_sig->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf.fsv_sig" />
- <option name="push" value="UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf" />
- <option name="push" value="UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf.fsv_sig->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf.fsv_sig" />
+ <option name="push" value="NotoSerif-Regular.ttf->/data/local/tmp/NotoSerif-Regular.ttf" />
+ <option name="push" value="NotoSerif-Bold.ttf->/data/local/tmp/NotoSerif-Bold.ttf" />
+ <option name="push" value="UpdatableSystemFontTest_NotoSerif-Regular.sig->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Regular.sig" />
+ <option name="push" value="UpdatableSystemFontTest_NotoSerif-Bold.sig->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Bold.sig" />
+ <option name="push" value="UpdatableSystemFontTest_NotoColorEmoji.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmoji.sig" />
+ <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiV0.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiV0.ttf" />
+ <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiV0.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiV0.sig" />
+ <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiVPlus1.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus1.ttf" />
+ <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig" />
+ <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf" />
+ <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
diff --git a/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/src/com/android/emojirenderingtestapp/EmojiRenderingTestActivity.java b/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/src/com/android/emojirenderingtestapp/EmojiRenderingTestActivity.java
index 947e9c2..a8c27fb0 100644
--- a/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/src/com/android/emojirenderingtestapp/EmojiRenderingTestActivity.java
+++ b/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/src/com/android/emojirenderingtestapp/EmojiRenderingTestActivity.java
@@ -20,6 +20,7 @@
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import android.app.Activity;
+import android.graphics.Typeface;
import android.os.Bundle;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -27,14 +28,20 @@
/** Test app to render an emoji. */
public class EmojiRenderingTestActivity extends Activity {
+ private static final String TEST_NOTO_SERIF = "test-noto-serif";
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout container = new LinearLayout(this);
container.setOrientation(LinearLayout.VERTICAL);
- TextView textView = new TextView(this);
- textView.setText("\uD83E\uDD72"); // 🥲
- container.addView(textView, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
+ TextView emojiTextView = new TextView(this);
+ emojiTextView.setText("\uD83E\uDD72"); // 🥲
+ container.addView(emojiTextView, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
+ TextView serifTextView = new TextView(this);
+ serifTextView.setTypeface(Typeface.create(TEST_NOTO_SERIF, Typeface.NORMAL));
+ serifTextView.setText(TEST_NOTO_SERIF);
+ container.addView(serifTextView, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
setContentView(container);
}
}
diff --git a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
index 6bd07d0..87fda0d 100644
--- a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
+++ b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
@@ -16,6 +16,9 @@
package com.android.updatablesystemfont;
+import static android.graphics.fonts.FontStyle.FONT_SLANT_UPRIGHT;
+import static android.graphics.fonts.FontStyle.FONT_WEIGHT_BOLD;
+import static android.graphics.fonts.FontStyle.FONT_WEIGHT_NORMAL;
import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
import static com.google.common.truth.Truth.assertThat;
@@ -30,6 +33,7 @@
import android.graphics.fonts.FontFamilyUpdateRequest;
import android.graphics.fonts.FontFileUpdateRequest;
import android.graphics.fonts.FontManager;
+import android.graphics.fonts.FontStyle;
import android.os.ParcelFileDescriptor;
import android.platform.test.annotations.RootPermissionTest;
import android.security.FileIntegrityManager;
@@ -77,31 +81,45 @@
private static final String SYSTEM_FONTS_DIR = "/system/fonts/";
private static final String DATA_FONTS_DIR = "/data/fonts/files/";
private static final String CERT_PATH = "/data/local/tmp/UpdatableSystemFontTestCert.der";
- private static final String NOTO_COLOR_EMOJI_POSTSCRIPT_NAME = "NotoColorEmoji";
- private static final String ORIGINAL_NOTO_COLOR_EMOJI_TTF =
+ private static final String NOTO_COLOR_EMOJI_POSTSCRIPT_NAME = "NotoColorEmoji";
+ private static final String NOTO_COLOR_EMOJI_TTF =
"/data/local/tmp/NotoColorEmoji.ttf";
- private static final String ORIGINAL_NOTO_COLOR_EMOJI_TTF_FSV_SIG =
- "/data/local/tmp/UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig";
+ private static final String NOTO_COLOR_EMOJI_SIG =
+ "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmoji.sig";
// A font with revision == 0.
private static final String TEST_NOTO_COLOR_EMOJI_V0_TTF =
- "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV0.ttf";
- private static final String TEST_NOTO_COLOR_EMOJI_V0_TTF_FSV_SIG =
- "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV0.ttf.fsv_sig";
+ "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiV0.ttf";
+ private static final String TEST_NOTO_COLOR_EMOJI_V0_SIG =
+ "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiV0.sig";
// A font with revision == original + 1
private static final String TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF =
- "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf";
- private static final String TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG =
- "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf.fsv_sig";
+ "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus1.ttf";
+ private static final String TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG =
+ "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig";
// A font with revision == original + 2
private static final String TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF =
- "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf";
- private static final String TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF_FSV_SIG =
- "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf.fsv_sig";
+ "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf";
+ private static final String TEST_NOTO_COLOR_EMOJI_VPLUS2_SIG =
+ "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig";
+
+ private static final String NOTO_SERIF_REGULAR_POSTSCRIPT_NAME = "NotoSerif";
+ private static final String NOTO_SERIF_REGULAR_TTF =
+ "/data/local/tmp/NotoSerif-Regular.ttf";
+ private static final String NOTO_SERIF_REGULAR_SIG =
+ "/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Regular.sig";
+
+ private static final String NOTO_SERIF_BOLD_POSTSCRIPT_NAME = "NotoSerif-Bold";
+ private static final String NOTO_SERIF_BOLD_TTF =
+ "/data/local/tmp/NotoSerif-Bold.ttf";
+ private static final String NOTO_SERIF_BOLD_SIG =
+ "/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Bold.sig";
private static final String EMOJI_RENDERING_TEST_APP_ID = "com.android.emojirenderingtestapp";
private static final String EMOJI_RENDERING_TEST_ACTIVITY =
EMOJI_RENDERING_TEST_APP_ID + "/.EmojiRenderingTestActivity";
+ // This should be the same as the one in EmojiRenderingTestActivity.
+ private static final String TEST_NOTO_SERIF = "test-noto-serif";
private static final long ACTIVITY_TIMEOUT_MILLIS = SECONDS.toMillis(10);
private static final String GET_AVAILABLE_FONTS_TEST_ACTIVITY =
@@ -141,11 +159,20 @@
@Test
public void updateFont() throws Exception {
+ FontConfig oldFontConfig =
+ SystemUtil.callWithShellPermissionIdentity(mFontManager::getFontConfig);
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
+ // Check that font config is updated.
String fontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(fontPath).startsWith(DATA_FONTS_DIR);
+ FontConfig newFontConfig =
+ SystemUtil.callWithShellPermissionIdentity(mFontManager::getFontConfig);
+ assertThat(newFontConfig.getConfigVersion())
+ .isGreaterThan(oldFontConfig.getConfigVersion());
+ assertThat(newFontConfig.getLastModifiedTimeMillis())
+ .isGreaterThan(oldFontConfig.getLastModifiedTimeMillis());
// The updated font should be readable and unmodifiable.
expectCommandToSucceed("dd status=none if=" + fontPath + " of=/dev/null");
expectCommandToFail("dd status=none if=" + CERT_PATH + " of=" + fontPath);
@@ -154,11 +181,11 @@
@Test
public void updateFont_twice() throws Exception {
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
String fontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS2_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
String fontPath2 = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(fontPath2).startsWith(DATA_FONTS_DIR);
@@ -173,16 +200,16 @@
public void updateFont_allowSameVersion() throws Exception {
// Update original font to the same version
assertThat(updateFontFile(
- ORIGINAL_NOTO_COLOR_EMOJI_TTF, ORIGINAL_NOTO_COLOR_EMOJI_TTF_FSV_SIG))
+ NOTO_COLOR_EMOJI_TTF, NOTO_COLOR_EMOJI_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
String fontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
String fontPath2 = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
// Update updated font to the same version
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
String fontPath3 = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(fontPath).startsWith(DATA_FONTS_DIR);
@@ -195,28 +222,58 @@
@Test
public void updateFont_invalidCert() throws Exception {
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS2_SIG))
.isEqualTo(FontManager.RESULT_ERROR_VERIFICATION_FAILURE);
}
@Test
public void updateFont_downgradeFromSystem() throws Exception {
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_V0_TTF, TEST_NOTO_COLOR_EMOJI_V0_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_V0_TTF, TEST_NOTO_COLOR_EMOJI_V0_SIG))
.isEqualTo(FontManager.RESULT_ERROR_DOWNGRADING);
}
@Test
public void updateFont_downgradeFromData() throws Exception {
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS2_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG))
.isEqualTo(FontManager.RESULT_ERROR_DOWNGRADING);
}
@Test
+ public void updateFontFamily() throws Exception {
+ assertThat(updateNotoSerifAs("serif")).isEqualTo(FontManager.RESULT_SUCCESS);
+ FontConfig.FontFamily family = findFontFamilyOrThrow("serif");
+ assertThat(family.getFontList()).hasSize(2);
+ assertThat(family.getFontList().get(0).getPostScriptName())
+ .isEqualTo(NOTO_SERIF_REGULAR_POSTSCRIPT_NAME);
+ assertThat(family.getFontList().get(0).getFile().getAbsolutePath())
+ .startsWith(DATA_FONTS_DIR);
+ assertThat(family.getFontList().get(0).getStyle().getWeight())
+ .isEqualTo(FONT_WEIGHT_NORMAL);
+ assertThat(family.getFontList().get(1).getPostScriptName())
+ .isEqualTo(NOTO_SERIF_BOLD_POSTSCRIPT_NAME);
+ assertThat(family.getFontList().get(1).getFile().getAbsolutePath())
+ .startsWith(DATA_FONTS_DIR);
+ assertThat(family.getFontList().get(1).getStyle().getWeight()).isEqualTo(FONT_WEIGHT_BOLD);
+ }
+
+ @Test
+ public void updateFontFamily_asNewFont() throws Exception {
+ assertThat(updateNotoSerifAs("UpdatableSystemFontTest-serif"))
+ .isEqualTo(FontManager.RESULT_SUCCESS);
+ FontConfig.FontFamily family = findFontFamilyOrThrow("UpdatableSystemFontTest-serif");
+ assertThat(family.getFontList()).hasSize(2);
+ assertThat(family.getFontList().get(0).getPostScriptName())
+ .isEqualTo(NOTO_SERIF_REGULAR_POSTSCRIPT_NAME);
+ assertThat(family.getFontList().get(1).getPostScriptName())
+ .isEqualTo(NOTO_SERIF_BOLD_POSTSCRIPT_NAME);
+ }
+
+ @Test
public void launchApp() throws Exception {
String fontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(fontPath).startsWith(SYSTEM_FONTS_DIR);
@@ -231,22 +288,25 @@
String originalFontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(originalFontPath).startsWith(SYSTEM_FONTS_DIR);
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
String updatedFontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(updatedFontPath).startsWith(DATA_FONTS_DIR);
+ updateNotoSerifAs(TEST_NOTO_SERIF);
+ String notoSerifPath = getFontPath(NOTO_SERIF_REGULAR_POSTSCRIPT_NAME);
startActivity(EMOJI_RENDERING_TEST_APP_ID, EMOJI_RENDERING_TEST_ACTIVITY);
// The original font should NOT be opened by the app.
SystemUtil.eventually(() -> {
assertThat(isFileOpenedBy(updatedFontPath, EMOJI_RENDERING_TEST_APP_ID)).isTrue();
assertThat(isFileOpenedBy(originalFontPath, EMOJI_RENDERING_TEST_APP_ID)).isFalse();
+ assertThat(isFileOpenedBy(notoSerifPath, EMOJI_RENDERING_TEST_APP_ID)).isTrue();
}, ACTIVITY_TIMEOUT_MILLIS);
}
@Test
public void reboot() throws Exception {
expectCommandToSucceed(String.format("cmd font update %s %s",
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG));
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG));
String fontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(fontPath).startsWith(DATA_FONTS_DIR);
@@ -264,7 +324,7 @@
Pattern.compile(Pattern.quote(TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF));
for (int i = 0; i < 10; i++) {
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
List<String> openFiles = getOpenFiles("system_server");
for (Pattern p : Arrays.asList(PATTERN_FONT_FILES, PATTERN_SYSTEM_FONT_FILES,
@@ -285,7 +345,7 @@
public void fdLeakTest_withoutPermission() throws Exception {
Pattern patternEmojiVPlus1 =
Pattern.compile(Pattern.quote(TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF));
- byte[] signature = Files.readAllBytes(Paths.get(TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG));
+ byte[] signature = Files.readAllBytes(Paths.get(TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG));
try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(
new File(TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF), MODE_READ_ONLY)) {
assertThrows(SecurityException.class,
@@ -340,18 +400,56 @@
configVersion);
}
+ private int updateNotoSerifAs(String familyName) throws IOException {
+ List<FontFamilyUpdateRequest.Font> fonts = Arrays.asList(
+ new FontFamilyUpdateRequest.Font.Builder(NOTO_SERIF_REGULAR_POSTSCRIPT_NAME,
+ new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT)).build(),
+ new FontFamilyUpdateRequest.Font.Builder(NOTO_SERIF_BOLD_POSTSCRIPT_NAME,
+ new FontStyle(FONT_WEIGHT_BOLD, FONT_SLANT_UPRIGHT)).build());
+ FontFamilyUpdateRequest.FontFamily fontFamily =
+ new FontFamilyUpdateRequest.FontFamily.Builder(familyName, fonts).build();
+ byte[] regularSig = Files.readAllBytes(Paths.get(NOTO_SERIF_REGULAR_SIG));
+ byte[] boldSig = Files.readAllBytes(Paths.get(NOTO_SERIF_BOLD_SIG));
+ try (ParcelFileDescriptor regularFd = ParcelFileDescriptor.open(
+ new File(NOTO_SERIF_REGULAR_TTF), MODE_READ_ONLY);
+ ParcelFileDescriptor boldFd = ParcelFileDescriptor.open(
+ new File(NOTO_SERIF_BOLD_TTF), MODE_READ_ONLY)) {
+ return SystemUtil.runWithShellPermissionIdentity(() -> {
+ FontConfig fontConfig = mFontManager.getFontConfig();
+ return mFontManager.updateFontFamily(new FontFamilyUpdateRequest.Builder()
+ .addFontFileUpdateRequest(
+ new FontFileUpdateRequest(regularFd, regularSig))
+ .addFontFileUpdateRequest(
+ new FontFileUpdateRequest(boldFd, boldSig))
+ .addFontFamily(fontFamily)
+ .build(), fontConfig.getConfigVersion());
+ });
+ }
+ }
+
private String getFontPath(String psName) {
- return SystemUtil.runWithShellPermissionIdentity(() -> {
- FontConfig fontConfig = mFontManager.getFontConfig();
- for (FontConfig.FontFamily family : fontConfig.getFontFamilies()) {
- for (FontConfig.Font font : family.getFontList()) {
- if (psName.equals(font.getPostScriptName())) {
- return font.getFile().getAbsolutePath();
- }
- }
- }
- throw new AssertionError("Font not found: " + psName);
- });
+ FontConfig fontConfig =
+ SystemUtil.runWithShellPermissionIdentity(mFontManager::getFontConfig);
+ return fontConfig.getFontFamilies().stream()
+ .flatMap(family -> family.getFontList().stream())
+ .filter(font -> psName.equals(font.getPostScriptName()))
+ // Return the last match, because the latter family takes precedence if two families
+ // have the same name.
+ .reduce((first, second) -> second)
+ .orElseThrow(() -> new AssertionError("Font not found: " + psName))
+ .getFile()
+ .getAbsolutePath();
+ }
+
+ private FontConfig.FontFamily findFontFamilyOrThrow(String familyName) {
+ FontConfig fontConfig =
+ SystemUtil.runWithShellPermissionIdentity(mFontManager::getFontConfig);
+ return fontConfig.getFontFamilies().stream()
+ .filter(family -> familyName.equals(family.getName()))
+ // Return the last match, because the latter family takes precedence if two families
+ // have the same name.
+ .reduce((first, second) -> second)
+ .orElseThrow(() -> new AssertionError("Family not found: " + familyName));
}
private static void startActivity(String appId, String activityId) throws Exception {
diff --git a/tests/UpdatableSystemFontTest/testdata/Android.bp b/tests/UpdatableSystemFontTest/testdata/Android.bp
index 0f01be0..1e6cb5e 100644
--- a/tests/UpdatableSystemFontTest/testdata/Android.bp
+++ b/tests/UpdatableSystemFontTest/testdata/Android.bp
@@ -41,9 +41,9 @@
}
genrule {
- name: "UpdatableSystemFontTestNotoColorEmojiV0Ttf",
- srcs: [":NotoColorEmojiTtf"],
- out: ["UpdatableSystemFontTestNotoColorEmojiV0.ttf"],
+ name: "UpdatableSystemFontTest_NotoColorEmojiV0.ttf",
+ srcs: [":NotoColorEmoji.ttf"],
+ out: ["UpdatableSystemFontTest_NotoColorEmojiV0.ttf"],
tools: ["update_font_metadata"],
cmd: "$(location update_font_metadata) " +
"--input=$(in) " +
@@ -52,9 +52,9 @@
}
genrule {
- name: "UpdatableSystemFontTestNotoColorEmojiVPlus1Ttf",
- srcs: [":NotoColorEmojiTtf"],
- out: ["UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf"],
+ name: "UpdatableSystemFontTest_NotoColorEmojiVPlus1.ttf",
+ srcs: [":NotoColorEmoji.ttf"],
+ out: ["UpdatableSystemFontTest_NotoColorEmojiVPlus1.ttf"],
tools: ["update_font_metadata"],
cmd: "$(location update_font_metadata) " +
"--input=$(in) " +
@@ -63,9 +63,9 @@
}
genrule {
- name: "UpdatableSystemFontTestNotoColorEmojiVPlus2Ttf",
- srcs: [":NotoColorEmojiTtf"],
- out: ["UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf"],
+ name: "UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf",
+ srcs: [":NotoColorEmoji.ttf"],
+ out: ["UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf"],
tools: ["update_font_metadata"],
cmd: "$(location update_font_metadata) " +
"--input=$(in) " +
@@ -87,29 +87,43 @@
}
genrule {
- name: "UpdatableSystemFontTestNotoColorEmojiTtfFsvSig",
+ name: "UpdatableSystemFontTest_NotoColorEmoji.sig",
defaults: ["updatable_system_font_sig_gen_default"],
- srcs: [":NotoColorEmojiTtf"],
- out: ["UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig"],
+ srcs: [":NotoColorEmoji.ttf"],
+ out: ["UpdatableSystemFontTest_NotoColorEmoji.sig"],
}
genrule {
- name: "UpdatableSystemFontTestNotoColorEmojiV0TtfFsvSig",
+ name: "UpdatableSystemFontTest_NotoColorEmojiV0.sig",
defaults: ["updatable_system_font_sig_gen_default"],
- srcs: [":UpdatableSystemFontTestNotoColorEmojiV0Ttf"],
- out: ["UpdatableSystemFontTestNotoColorEmojiV0.ttf.fsv_sig"],
+ srcs: [":UpdatableSystemFontTest_NotoColorEmojiV0.ttf"],
+ out: ["UpdatableSystemFontTest_NotoColorEmojiV0.sig"],
}
genrule {
- name: "UpdatableSystemFontTestNotoColorEmojiVPlus1TtfFsvSig",
+ name: "UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig",
defaults: ["updatable_system_font_sig_gen_default"],
- srcs: [":UpdatableSystemFontTestNotoColorEmojiVPlus1Ttf"],
- out: ["UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf.fsv_sig"],
+ srcs: [":UpdatableSystemFontTest_NotoColorEmojiVPlus1.ttf"],
+ out: ["UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig"],
}
genrule {
- name: "UpdatableSystemFontTestNotoColorEmojiVPlus2TtfFsvSig",
+ name: "UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig",
defaults: ["updatable_system_font_sig_gen_default"],
- srcs: [":UpdatableSystemFontTestNotoColorEmojiVPlus2Ttf"],
- out: ["UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf.fsv_sig"],
+ srcs: [":UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf"],
+ out: ["UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig"],
+}
+
+genrule {
+ name: "UpdatableSystemFontTest_NotoSerif-Regular.sig",
+ defaults: ["updatable_system_font_sig_gen_default"],
+ srcs: [":NotoSerif-Regular.ttf"],
+ out: ["UpdatableSystemFontTest_NotoSerif-Regular.sig"],
+}
+
+genrule {
+ name: "UpdatableSystemFontTest_NotoSerif-Bold.sig",
+ defaults: ["updatable_system_font_sig_gen_default"],
+ srcs: [":NotoSerif-Bold.ttf"],
+ out: ["UpdatableSystemFontTest_NotoSerif-Bold.sig"],
}
diff --git a/tests/utils/testutils/java/com/android/internal/util/test/FsUtil.java b/tests/utils/testutils/java/com/android/internal/util/test/FsUtil.java
new file mode 100644
index 0000000..e656612
--- /dev/null
+++ b/tests/utils/testutils/java/com/android/internal/util/test/FsUtil.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util.test;
+
+import java.io.File;
+
+public class FsUtil {
+
+ /**
+ * Deletes all files under a given directory. Deliberately ignores errors, on the assumption
+ * that test cleanup is only supposed to be best-effort.
+ *
+ * @param dir directory to clear its contents
+ */
+ public static void deleteContents(File dir) {
+ File[] files = dir.listFiles();
+ if (files != null) {
+ for (File file : files) {
+ if (file.isDirectory()) {
+ deleteContents(file);
+ }
+ file.delete();
+ }
+ }
+ }
+}
diff --git a/tests/vcn/OWNERS b/tests/vcn/OWNERS
index 33b9f0f..2441e77 100644
--- a/tests/vcn/OWNERS
+++ b/tests/vcn/OWNERS
@@ -3,5 +3,5 @@
benedictwong@google.com
ckesting@google.com
evitayan@google.com
+junyin@google.com
nharold@google.com
-jchalard@google.com
\ No newline at end of file
diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp
index 96f6512..ebe41da 100644
--- a/tools/aapt2/SdkConstants.cpp
+++ b/tools/aapt2/SdkConstants.cpp
@@ -27,7 +27,7 @@
static ApiVersion sDevelopmentSdkLevel = 10000;
static const auto sDevelopmentSdkCodeNames = std::unordered_set<StringPiece>({
- "Q", "R", "S"
+ "Q", "R", "S", "T"
});
static const std::vector<std::pair<uint16_t, ApiVersion>> sAttrIdMap = {
diff --git a/tools/aosp/aosp_sha.sh b/tools/aosp/aosp_sha.sh
index 81d35ef..3cdb27c 100755
--- a/tools/aosp/aosp_sha.sh
+++ b/tools/aosp/aosp_sha.sh
@@ -8,7 +8,21 @@
# Change is explicitly marked as ok to skip AOSP
exit 0
else
- # Change appears to be non-AOSP; search for files
+ # Change appears to be non-AOSP.
+
+ # If this is a cherry-pick, then allow it.
+ cherrypick=0
+ while read -r line ; do
+ if [[ $line =~ cherry\ picked\ from ]] ; then
+ (( cherrypick++ ))
+ fi
+ done < <(git show $1)
+ if (( cherrypick != 0 )); then
+ # This is a cherry-pick, so allow it.
+ exit 0
+ fi
+
+ # See if any files are affected.
count=0
while read -r file ; do
if (( count == 0 )); then
diff --git a/tools/codegen/src/com/android/codegen/Utils.kt b/tools/codegen/src/com/android/codegen/Utils.kt
index 7cfa784..9ceb204 100644
--- a/tools/codegen/src/com/android/codegen/Utils.kt
+++ b/tools/codegen/src/com/android/codegen/Utils.kt
@@ -43,8 +43,8 @@
* cccc dd
*/
fun Iterable<Pair<String, String>>.columnize(separator: String = " | "): String {
- val col1w = map { (a, _) -> a.length }.max()!!
- val col2w = map { (_, b) -> b.length }.max()!!
+ val col1w = map { (a, _) -> a.length }.maxOrNull()!!
+ val col2w = map { (_, b) -> b.length }.maxOrNull()!!
return map { it.first.padEnd(col1w) + separator + it.second.padEnd(col2w) }.joinToString("\n")
}